命令行操作windows路由表
- 在windows系统中,查看路由表
route print
接口列列表:
- 添加路由表
route add 192.168.2.0 mask 255.255.255.0 192.168.1.1 metric 10 if 25
- 这里的第一个IP地址是目标IP地址
mask后面跟随的是掩码- 掩码后面跟随的是网关(下一跳IP地址)
metric表示优先级 (可省略)if是网卡索引(可省略)
- 删除路由表
route delete 192.168.1.0 mask 255.255.255.0 192.168.1.1 metric 10 if 2
删除的时候,掩码跟网关都可以是省略.
基础知识
网络通信通常遵循OSI模型(开放系统互联模型)和TCP/IP模型。以下是这两个模型的层级结构及其功能:
1. OSI 模型
OSI 模型分为七层,每层都有其特定的功能:
-
物理层(Physical Layer) :
- 负责物理连接的建立和维护,传输原始比特流(0和1)。
- 示例:电缆、网卡、信号传输等。
-
数据链路层(Data Link Layer) :
- 提供节点间的数据传输,负责错误检测和纠正。
- 示例:以太网、Wi-Fi、PPP(点对点协议)。
-
网络层(Network Layer) :
- 负责数据包的路由和转发,提供逻辑地址(如 IP 地址)。
- 示例:IP(IPv4、IPv6)、ICMP(互联网控制消息协议)。
-
传输层(Transport Layer) :
- 负责端到端的通信,提供可靠性和流量控制。
- 示例:TCP(传输控制协议)、UDP(用户数据报协议)。
-
会话层(Session Layer) :
- 管理会话的建立、维护和终止,负责数据交换的同步。
- 示例:NetBIOS、RPC(远程过程调用)。
-
表示层(Presentation Layer) :
- 负责数据格式的转换和加密,确保应用层可以理解数据。
- 示例:JPEG、ASCII、SSL/TLS。
-
应用层(Application Layer) :
- 用户直接交互的层,提供应用程序服务。
- 示例:HTTP、FTP、SMTP、DNS。
2. TCP/IP 模型
TCP/IP 模型是互联网的基础,通常分为四层:
应用层
|
----------------
| TCP/UDP | <- 传输层
----------------
| IP | <- 网络层(IP数据包)
----------------
| Ethernet | <- 数据链路层
----------------
-
网络接口层(Network Interface Layer) :
- 对应于 OSI 模型的物理层和数据链路层,负责物理传输和数据帧的处理。
-
互联网层(Internet Layer) :
- 对应于 OSI 模型的网络层,负责数据包的路由和寻址。
- 示例:IP、ICMP。
-
传输层(Transport Layer) :
- 对应于 OSI 模型的传输层,负责端到端的通信。
- 示例:TCP、UDP。
-
应用层(Application Layer) :
- 对应于 OSI 模型的会话层、表示层和应用层,提供应用程序服务。
- 示例:HTTP、FTP、SMTP、DNS。
根据TCP/IP 四层网络架构协议,我们对流量的转发,只需要处理以太网帧这一层即可,上层的IP数据包(网络层),TCP/UDP(传输层) 以及应用层的所有数据均能进行转发.
TCP/IP 4层数据结构代码解析
数据结构示意图(ICMP与TCP UDP 并列):
包含关系如下:
以太网帧 -> IP数据包 -> IPV4/IPV6 -> TCP/UDP/ICMP...
我们一般只需要关注解析对应的头,里面的负载的数据,不做解析.
数据链路层 以太网帧数据结构
常用的标准结构有两种: Ethernet II IEEE 802.3
互联网使用的是: Ethernet II
参考链接: support.huawei.com/enterprise/…
+-------------------+-------------------+-------------------+-------------------+
| 前导部分 (Preamble) | 帧头 (Frame Header) | 有效载荷 (Payload) | 帧尾 (Frame Trailer) |
+-------------------+-------------------+-------------------+-------------------+
| 7 字节 | 14 字节 | 46-1500 字节 | 4 字节 |
+-------------------+-------------------+-------------------+-------------------+
一般而言,我们只需要关注帧头+负载
+------------------+-------------------+------------------+------------------+
| 目的 MAC 地址 | 源 MAC 地址 | EtherType (2) | 数据 payload |
| (6 字节) | (6 字节) | (2 字节) | (46-1500 字节) |
+------------------+-------------------+------------------+------------------+
| FCS (4 字节) |
+------------------+
也就是前14个字节.对应的结构体可以定义为:
typedef struct {
// uint8_t preamble[7]; // 前导码 (7 字节)
// uint8_t sfd; // SFD (1 字节)
uint8_t destinationMAC[6]; // 目的 MAC 地址 (6 字节)
uint8_t sourceMAC[6]; // 源 MAC 地址 (6 字节)
uint16_t etherType; // 以太网类型 (EtherType, 2 字节) - 高 8 位表示协议类型
uint8_t payload[]; // 负载数据 (最大 1500 字节)
// uint32_t frameCheckSequence; // 帧校验序列 (FCS, 4 字节)
} EthernetFrame, * PEthernetFrame;
// 以太网类型定义
typedef struct {
uint16_t etherType; // 以太网类型值
const char* description; // 类型描述
}EtherType, * PEtherType;
static
void GetEthType(uint16_t etherType, EtherType& typeInfo) {
typeInfo.etherType = etherType;
switch (etherType) {
case 0x0800:
typeInfo.description = "IPv4";
break;
case 0x86DD:
typeInfo.description = "IPv6";
break;
case 0x0806:
typeInfo.description = "ARP";
break;
default:
typeInfo.description = "Unknown";
break;
}
}
传输层 IP数据包解析
IP数据包无论是IPV4还是IPV6首字节的高4位表示IP的版本,所以可以先根据高4位来决定走IPV4还是IPV6
参考文档: support.huawei.com/enterprise/…
// 解析IP数据包
bool ParseIpPacket(const uint8_t* packet, size_t length) {
if (length < sizeof(IPv4Packet) && length < sizeof(IPv6Packet)) {
Log(WINTUN_LOG_INFO, L"数据包不是以太网帧");
return false;
}
// 获取IP版本
uint8_t version = (packet[0] >> 4) & 0xF;
// 解析IP数据包
if (version == 4)
{
return ParseIPv4Packet(packet, length);
}
else if (version == 6) { // IPv6
return ParseIPv6Packet(packet, length);
}
else {
Log(WINTUN_LOG_INFO, L"未知IP数据包: %u 数据包长度: %d", version, length);
}
return false;
}
IPV4 头结构定义-解析
IPV4结构定义:
// 定义 IPv4 数据包结构体
typedef struct {
uint8_t version; // 高 4 位表示版本, 低 4 位表示头部长度
uint8_t typeOfService; // 服务类型 (Type of Service)
uint16_t totalLength; // 总长度 (Total Length)
uint16_t identification; // 标识 (Identification)
uint16_t flags; // 高 3 位表示标志, 低 13 位表示片偏移
uint8_t ttl; // 生存时间 (Time to Live)
uint8_t protocol; // 协议 (Protocol)
uint16_t headerChecksum; // 校验和 (Header Checksum)
uint32_t sourceAddress; // 源地址 (Source Address)
uint32_t destinationAddress; // 目的地址 (Destination Address)
uint8_t data[]; // 可变长度有效负载
} IPv4Packet, * PIPv4Packet;
// IPv4 版本结构体
typedef struct {
uint8_t version; // IPv4 版本
uint8_t headerLength; // 头部长度(单位为 32 位字)
}IPv4Version, * PIPv4Version;
static
void ParseIpv4Version(uint8_t version, IPv4Version& info) {
info.version = (version >> 4) & 0x0F; // 获取版本(高 4 位)
info.headerLength = version & 0x0F; // 获取头部长度(低 4 位)
}
// 标志和片偏移结构体
typedef struct {
uint8_t reserved; // 保留位 高1位
uint8_t notFragment; // 不分片标志 高2位
uint8_t moreFragments; // 更多分片标志 高3位
uint16_t fragmentOffset; // 片偏移 后面的13位
}Ipv4Flags, * PIpv4Flags;
// 静态方法:解析标志和片偏移
static
void ParseIpv4Flags(uint16_t flags, Ipv4Flags& flagsInfo) {
flagsInfo.reserved = (flags >> 14) & 0x01; // 保留位 高1位
flagsInfo.notFragment = (flags >> 13) & 0x01; // 不分片标志 高1位
flagsInfo.moreFragments = (flags >> 12) & 0x01; // 更多分片标志 高1位
flagsInfo.fragmentOffset = flags & 0x0FFF; // 片偏移 后面的13位
}
IPV6 头结构定义-解析
// 定义 IPv6 数据包结构体
typedef struct {
uint32_t version; // 高 4 位表示版本, 8 位表示流量类, 20 位表示流标签
uint16_t payloadLength; // 负载长度 (Payload Length)
uint8_t nextHeader; // 下一个头部类型 (Next Header)
uint8_t hopLimit; // 跳数限制 (Hop Limit)
uint8_t sourceAddress[16]; // 源地址 (Source Address, 128 位)
uint8_t destinationAddress[16]; // 目的地址 (Destination Address, 128 位)
uint8_t data[]; // 可变长度:有效负载
} IPv6Packet, * PIPv6Packet;
// 版本、流量类别和流标签结构体
typedef struct {
uint8_t version; // 版本(高 4 位)
uint8_t trafficClass; // 流量类别(中 8 位)
uint32_t flowLabel; // 流标签(低 20 位)
} Ipv6Version, * PIpv6Version;
// 静态方法:解析版本、流量类别和流标签
static void ParseIpv6Version(uint32_t vtcfl, Ipv6Version& info) {
info.version = (vtcfl >> 28) & 0x0F; // 获取版本(高 4 位)
info.trafficClass = (vtcfl >> 20) & 0xFF; // 获取流量类别(中 8 位)
info.flowLabel = vtcfl & 0xFFFFF; // 获取流标签(低 20 位)
}
TCP头结构定义-解析
// 定义 TCP 数据包结构体
typedef struct {
uint16_t srcPort; // 源端口
uint16_t destPort; // 目的端口
uint32_t sequenceNumber; // 序列号
uint32_t acknowledgmentNumber; // 确认号
uint8_t dataOffsetAndFlags; // 数据偏移和标志
uint8_t windowSize; // 窗口大小
uint16_t checksum; // 校验和
uint16_t urgentPointer; // 紧急指针
uint8_t data[]; // 有效负载(可变长度)
} TCPPacket, * PTCPPacket;
UDP头结构定义-解析
// 定义 UDP 数据包结构体
typedef struct {
uint16_t srcPort; // 源端口
uint16_t destPort; // 目的端口
uint16_t length; // 长度
uint16_t checksum; // 校验和
uint8_t data[]; // 有效负载(可变长度)
} UDPPacket, * PUDPPacket;
ICMP头结构定义-解析
typedef struct {
uint8_t type; // ICMP 类型 (1 字节) 0: 回显应答 (Echo Reply)// 8: 回显请求 (Echo Request)// 3: 目的不可达 (Destination Unreachable)// 11: 超时 (Time Exceeded)// 5: 重定向 (Redirect)
uint8_t code; // ICMP 代码 (1 字节) 进一步细分 ICMP 类型的原因// 例如,对于目的不可达:// 0: 网络不可达// 1: 主机不可达// 2: 协议不可达// 3: 端口不可达
uint16_t checksum; // 校验和 (2 字节) 用于错误检测的校验和,计算方法为将整个 ICMP 头部和数据部分的 16 位字进行求和然后取反。
uint16_t identifier; // 标识符 (2 字节) 用于标识请求和响应之间的对应关系,通常在使用 ping 时会用到。
uint16_t sequence; // 序列号 (2 字节) 用于跟踪 ICMP 消息的顺序,特别是在 ping 请求中。
size_t dataLength; // 数据长度 (可选)
uint8_t data[]; // 可变长度数据 (依赖于类型) 传输的附加数据,通常包含时间戳、负载等信息。
} ICMPPacket, * PICMPPacket;
使用wintun进行数据发送与获取
wintun 发送的数据包都是IP数据包,接收的数据包也是IP数据包,通过wintun发送出去的数据,接收函数不会收到.
初始化,创建虚拟适配器
static WINTUN_CREATE_ADAPTER_FUNC* WintunCreateAdapter;
static WINTUN_CLOSE_ADAPTER_FUNC* WintunCloseAdapter;
static WINTUN_OPEN_ADAPTER_FUNC* WintunOpenAdapter;
static WINTUN_GET_ADAPTER_LUID_FUNC* WintunGetAdapterLUID;
static WINTUN_GET_RUNNING_DRIVER_VERSION_FUNC* WintunGetRunningDriverVersion;
static WINTUN_DELETE_DRIVER_FUNC* WintunDeleteDriver;
static WINTUN_SET_LOGGER_FUNC* WintunSetLogger;
static WINTUN_START_SESSION_FUNC* WintunStartSession;
static WINTUN_END_SESSION_FUNC* WintunEndSession;
static WINTUN_GET_READ_WAIT_EVENT_FUNC* WintunGetReadWaitEvent;
static WINTUN_RECEIVE_PACKET_FUNC* WintunReceivePacket;
static WINTUN_RELEASE_RECEIVE_PACKET_FUNC* WintunReleaseReceivePacket;
static WINTUN_ALLOCATE_SEND_PACKET_FUNC* WintunAllocateSendPacket;
static WINTUN_SEND_PACKET_FUNC* WintunSendPacket;
static HMODULE
InitializeWintun(void)
{
HMODULE Wintun =
LoadLibraryExW(L"wintun.dll", NULL, LOAD_LIBRARY_SEARCH_APPLICATION_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
if (!Wintun)
return NULL;
#define X(Name) ((*(FARPROC *)&Name = GetProcAddress(Wintun, #Name)) == NULL)
if (X(WintunCreateAdapter) || X(WintunCloseAdapter) || X(WintunOpenAdapter) || X(WintunGetAdapterLUID) ||
X(WintunGetRunningDriverVersion) || X(WintunDeleteDriver) || X(WintunSetLogger) || X(WintunStartSession) ||
X(WintunEndSession) || X(WintunGetReadWaitEvent) || X(WintunReceivePacket) || X(WintunReleaseReceivePacket) ||
X(WintunAllocateSendPacket) || X(WintunSendPacket))
#undef X
{
DWORD LastError = GetLastError();
FreeLibrary(Wintun);
SetLastError(LastError);
return NULL;
}
return Wintun;
}
这段代码在wintun 官方的示例代码中即可找到.git.zx2c4.com/wintun/tree…
创建网络适配器(虚拟网卡)
// 创建适配器
GUID netGuid;
CLSIDFromString(adapterGuid.c_str(), &netGuid);
// 打开适配器
WINTUN_ADAPTER_HANDLE Adapter = WintunOpenAdapter(adapterName.c_str());
if (Adapter == NULL)
{
Adapter = WintunCreateAdapter(adapterName.c_str(), (adapterName + L"-net").c_str(), &netGuid);
}
if (!Adapter)
{
LogError(L"创建网络适配器失败", GetLastError());
CleanupQuite();
return;
}
添加虚拟网卡的单播地址(这一步也可以直接添加路由来实现)
// 创建单播地址
MIB_UNICASTIPADDRESS_ROW row;
InitializeUnicastIpAddressEntry(&row);
// 将单播地址绑定到适配器网卡
WintunGetAdapterLUID(Adapter, &row.InterfaceLuid);
row.Address.Ipv4.sin_family = AF_INET;// ipv4
row.Address.Ipv4.sin_addr.S_un.S_addr = inet_addr(ip.c_str());
row.ValidLifetime = 0xffffffff;
row.OnLinkPrefixLength = 24; /* This is a /24 network */
DWORD LastError = CreateUnicastIpAddressEntry(&row);
if (LastError != ERROR_SUCCESS && LastError != ERROR_OBJECT_ALREADY_EXISTS)
{
LogError(L"设置IP地址失败", LastError);
CleanupWorkers(workers, Session);
return;
}
开启会话拦截
Session = WintunStartSession(Adapter, 0x400000);
if (!Session)
{
// 启动会话失败
LogError(L"启动会话失败", GetLastError());
CleanupWorkers(workers, Session);
return;
}
创建1收1发线程
workers[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReceivePackets, (LPVOID)Session, 0, NULL);
workers[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SendPackets, (LPVOID)Session, 0, NULL);
发送数据
// 发送数据包
static DWORD WINAPI
SendPackets(_Inout_ DWORD_PTR SessionPtr)
{
WINTUN_SESSION_HANDLE Session = (WINTUN_SESSION_HANDLE)SessionPtr;
while (!HaveQuit)
{
// BYTE* Packet = WintunAllocateSendPacket(Session, 28);
// if (Packet)
// {
//
// MakeICMP(Packet);
//
// uint8_t version = (Packet[0] >> 4) & 0xF;
// uint8_t protocol = Packet[9];
// Log(WINTUN_LOG_INFO, L"Send Packet: %u, protocol: %u, PacketSize: %d", version, protocol, 28);
//
// SetLastError(0);
// WintunSendPacket(Session, Packet);
//
// //
// DWORD lastError = GetLastError();
// if (lastError != ERROR_SUCCESS)
// {
// Log(WINTUN_LOG_INFO, L"Send Packet: Error Code : %d", lastError);
// }
//
// }
// else if (GetLastError() != ERROR_BUFFER_OVERFLOW)
// {
// return LogLastError(L"Packet write failed");
// }
// 延时 1 秒
switch (WaitForSingleObject(QuitEvent, 1000 /* 1 second */))
{
case WAIT_ABANDONED:
case WAIT_OBJECT_0:
return ERROR_SUCCESS;
}
}
return ERROR_SUCCESS;
}
接收数据
// 接收数据包
static DWORD WINAPI
ReceivePackets(_Inout_ DWORD_PTR SessionPtr)
{
WINTUN_SESSION_HANDLE Session = (WINTUN_SESSION_HANDLE)SessionPtr;
HANDLE WaitHandles[] = { WintunGetReadWaitEvent(Session), QuitEvent };
while (!HaveQuit)
{
DWORD PacketSize;
BYTE* Packet = WintunReceivePacket(Session, &PacketSize);
if (Packet)
{
PrintPacket(Packet, PacketSize);
// 获取IP版本
uint8_t version = (Packet[0] >> 4) & 0xF;
uint8_t protocol = Packet[9];
Log(WINTUN_LOG_INFO, L"Receive Packet: %u, protocol: %u, PacketSize: %d", version, protocol, PacketSize);
if (version == 4)
{
IPv4Packet* ipv4Packet = (IPv4Packet*)Packet;
if (ipv4Packet->protocol == 1)
{
HandleIcmpPacket(ipv4Packet, PacketSize, Session);
}
else
{
WintunReleaseReceivePacket(Session, Packet);
}
}
else
{
WintunReleaseReceivePacket(Session, Packet);
}
}
else
{
DWORD LastError = GetLastError();
switch (LastError)
{
case ERROR_NO_MORE_ITEMS:
if (WaitForMultipleObjects(_countof(WaitHandles), WaitHandles, FALSE, INFINITE) == WAIT_OBJECT_0)
{
continue;
}
return ERROR_SUCCESS;
default:
LogError(L"Packet read failed", LastError);
return LastError;
}
}
}
return ERROR_SUCCESS;
}
我使用ICMP协议进行测试,是可以正常接收并应答的.
windows 路由相关代码
获取路由表
inet_ntoa 函数,返回的是一个静态地址,所以每次调用,该地址的值都会重置.所以调用后需要拷贝到一个新的地址中.
void GetTheRoute(_In_ DWORD forwardType, _In_ DWORD proto, _Out_ std::string& destination, _Out_ std::string& subnetMask, _Out_ std::string& gateway, _Out_ DWORD& netInterIdx)
{
// 默认赋值-1
netInterIdx = -1;
// 获取路由表的大小
DWORD dwSize = 0;
DWORD dwRetVal = GetIpForwardTable(nullptr, &dwSize, FALSE);
if (dwRetVal != ERROR_INSUFFICIENT_BUFFER) {
std::cerr << "GetIpForwardTable failed with error: " << dwRetVal << '\n';
return;
}
// 分配内存以存储路由表
MIB_IPFORWARDTABLE* pIpForwardTable = (MIB_IPFORWARDTABLE*)malloc(dwSize);
if (pIpForwardTable == nullptr) {
std::cerr << "Memory allocation failed!" << '\n';
return;
}
// 获取路由表
dwRetVal = GetIpForwardTable(pIpForwardTable, &dwSize, FALSE);
if (dwRetVal != NO_ERROR) {
std::cerr << "GetIpForwardTable failed with error: " << dwRetVal << '\n';
free(pIpForwardTable);
return;
}
for (DWORD i = 0; i < pIpForwardTable->dwNumEntries; i++) {
MIB_IPFORWARDROW& route = pIpForwardTable->table[i];
IN_ADDR destAddr, maskAddr, nextHopAddr;
destAddr.S_un.S_addr = route.dwForwardDest;
maskAddr.S_un.S_addr = route.dwForwardMask;
nextHopAddr.S_un.S_addr = route.dwForwardNextHop;
std::string _destIp = inet_ntoa(destAddr);
std::string _mask = inet_ntoa(maskAddr);
std::string _gateway = inet_ntoa(nextHopAddr);
std::cout
<< "目的IP: "
<< _destIp
<< " 子网掩码: "
<< _mask
<< " 网关: "
<< _gateway
<< " 网卡索引: "
<< route.dwForwardIfIndex
<< " 路由类型: "
<< route.dwForwardType
<< " 协议: "
<< route.dwForwardProto
<< "优先级: "
<< route.dwForwardMetric1
<< '\n';
// 找到指定路由表
if (route.dwForwardType == forwardType && route.dwForwardProto == proto)
{
destination = destination.empty() ? _destIp : destination;
subnetMask = subnetMask.empty() ? _mask : subnetMask;
gateway = gateway.empty() ? _gateway : gateway;
netInterIdx = route.dwForwardIfIndex;
}
}
// 释放内存
free(pIpForwardTable);
}
windows获取网络适配器
void GetAdapterFromIfIdx(_In_ DWORD& interIdx, _Out_ std::string& ipv4, _Out_ std::string& ipv6) {
ULONG outBufLen = 0;
// Get required buffer size
GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &outBufLen);
PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)malloc(outBufLen);
// 获取适配器列表
ULONG adResult = GetAdaptersAddresses(AF_UNSPEC, 0, NULL, pAddresses, &outBufLen);
if (adResult != NO_ERROR)
{
free(pAddresses);
return;
}
// 循环遍历适配器列表
while (pAddresses != NULL)
{
std::cout << "适配器名称: " << pAddresses->AdapterName << '\n';
std::wcout << "描述: " << pAddresses->Description << '\n';
// 输出物理地址(MAC地址)
std::cout << "MAC地址: ";
std::cout
<< std::hex
<< std::setw(2) << std::setfill('0')
<< std::uppercase;
for (UINT i = 0; i < pAddresses->PhysicalAddressLength; ++i) {
std::cout
<< static_cast<int>(pAddresses->PhysicalAddress[i]);
// 分隔符
if (i < pAddresses->PhysicalAddressLength - 1) {
std::cout << ":";
}
}
std::cout
<< std::dec
<< "\n";
// 输出其他信息
std::cout << "IPv4 优先级: " << pAddresses->Ipv4Metric << '\n';
std::cout << "IPv6 优先级: " << pAddresses->Ipv6Metric << '\n';
std::cout << "操作状态: " << pAddresses->OperStatus << '\n';
std::cout << "接口序号: " << pAddresses->IfIndex << '\n';
std::cout << "接口类型: " << pAddresses->IfType << '\n';
// 输出 DNS Suffix
if (pAddresses->DnsSuffix) {
std::wcout << "DNS 后缀: " << pAddresses->DnsSuffix << '\n';
}
// 输出 Network GUID
wchar_t guidString[39]; // 36 for GUID + 3 for braces and null terminator
StringFromGUID2(pAddresses->NetworkGuid, guidString, sizeof(guidString));
std::wcout << "GUID: " << guidString << '\n';
// 输出第一个单播地址
if (pAddresses->FirstUnicastAddress) {
std::cout << "单播地址: " << "\n";
for (IP_ADAPTER_UNICAST_ADDRESS* addr = pAddresses->FirstUnicastAddress; addr != nullptr; addr = addr->Next) {
std::cout
<< "协议: " << addr->Address.lpSockaddr->sa_family
<< " 长度: " << addr->Address.iSockaddrLength
<< " 是否ipv4: " << ((addr->Address.lpSockaddr->sa_family == AF_INET) ? "是" : "否") << '\n';
std::string ip;
GetIp(addr->Address.lpSockaddr, ip);
std::cout
<< " ip: "
<< ip
<< '\n';
if (pAddresses->IfIndex == interIdx)
{
if (addr->Address.lpSockaddr->sa_family == AF_INET)
{
// ipv4
ipv4 = ip;
}
else
{
ipv6 = ip;
}
}
}
}
// 输出第一个任播地址
if (pAddresses->FirstAnycastAddress) {
std::cout << "任播地址(多播地址): " << "\n";
for (IP_ADAPTER_ANYCAST_ADDRESS* addr = pAddresses->FirstAnycastAddress; addr != nullptr; addr = addr->Next)
{
std::cout
<< "协议: " << addr->Address.lpSockaddr->sa_family
<< " 长度: " << addr->Address.iSockaddrLength
<< " 是否ipv4: " << ((addr->Address.lpSockaddr->sa_family == AF_INET) ? "是" : "否") << '\n';
std::string ip;
GetIp(addr->Address.lpSockaddr, ip);
std::cout
<< " ip: "
<< ip
<< '\n';
}
}
// 输出第一个组播地址
if (pAddresses->FirstMulticastAddress) {
std::cout << "组播地址: " << "\n";
for (IP_ADAPTER_MULTICAST_ADDRESS* addr = pAddresses->FirstMulticastAddress; addr != nullptr; addr = addr->Next) {
std::cout
<< "协议: " << addr->Address.lpSockaddr->sa_family
<< " 长度: " << addr->Address.iSockaddrLength
<< " 是否ipv4: " << ((addr->Address.lpSockaddr->sa_family == AF_INET) ? "是" : "否") << '\n';
std::string ip;
GetIp(addr->Address.lpSockaddr, ip);
std::cout
<< " ip: "
<< ip
<< '\n';
}
}
// 输出第一个DNS服务器地址
if (pAddresses->FirstDnsServerAddress) {
std::cout << "DNS: " << "\n";
for (IP_ADAPTER_DNS_SERVER_ADDRESS* addr = pAddresses->FirstDnsServerAddress; addr != nullptr; addr = addr->Next) {
std::cout
<< "协议: " << addr->Address.lpSockaddr->sa_family
<< " 长度: " << addr->Address.iSockaddrLength
<< " 是否ipv4: " << ((addr->Address.lpSockaddr->sa_family == AF_INET) ? "是" : "否") << '\n';
std::string ip;
GetIp(addr->Address.lpSockaddr, ip);
std::cout
<< " ip: "
<< ip
<< '\n';
}
}
std::cout << "\n";
pAddresses = pAddresses->Next;
}
free(pAddresses);
}
创建路由表
void AddRoute(_In_ DWORD forwardType, _In_ DWORD proto, _In_ const std::string& _destIp, _In_ const std::string& _mask, _In_ const std::string& _gateway, _In_ DWORD netInterIdx) {
DWORD dwError;
MIB_IPFORWARDROW row;
// 将字符串转换为网络地址
IN_ADDR destAddr, maskAddr, gwAddr;
destAddr.s_addr = inet_addr(_destIp.c_str());
maskAddr.s_addr = inet_addr(_mask.c_str());
gwAddr.s_addr = inet_addr(_gateway.c_str());
// 检查地址是否有效
if (destAddr.s_addr == INADDR_NONE || maskAddr.s_addr == INADDR_NONE || gwAddr.s_addr == INADDR_NONE) {
std::cerr << "error ip" << std::endl;
return;
}
// 初始化路由行
ZeroMemory(&row, sizeof(row));
// 填充路由行
row.dwForwardDest = destAddr.S_un.S_addr; // 目标地址
row.dwForwardMask = maskAddr.S_un.S_addr; // 子网掩码
row.dwForwardNextHop = gwAddr.S_un.S_addr;// 网关(下一跳的网关)
row.dwForwardIfIndex = netInterIdx; // 接口
row.dwForwardType = forwardType; // 跳转类型
row.dwForwardProto = proto; // 跳转协议
//row.dwForwardAge = 0; // 路由年龄
row.dwForwardMetric1 = 2; // 优先级
// 添加路由表
dwError = CreateIpForwardEntry(&row);
if (dwError == NO_ERROR) {
std::cout
<< "add route success!"
<< '\n';
}
else {
std::cerr
<< "add route fail: "
<< dwError
<< '\n';
}
}
删除路由表
void DeleteRoute(const std::string& destination, const std::string& subnetMask, const std::string& gateway, DWORD netInterIdx) {
DWORD dwError;
MIB_IPFORWARDROW row;
IN_ADDR destAddr, maskAddr, gwAddr;
destAddr.s_addr = inet_addr(destination.c_str());
maskAddr.s_addr = inet_addr(subnetMask.c_str());
gwAddr.s_addr = inet_addr(gateway.c_str());
// 填充路由行
row.dwForwardIfIndex = netInterIdx; // 接口
row.dwForwardDest = destAddr.S_un.S_addr;
row.dwForwardMask = maskAddr.S_un.S_addr;
row.dwForwardNextHop = gwAddr.S_un.S_addr;// 网关(下一跳的网关)
// 删除路由
dwError = DeleteIpForwardEntry(&row);
if (dwError == NO_ERROR) {
std::cout << "路由删除成功!" << std::endl;
}
else {
std::cerr << "删除路由失败,错误代码: " << dwError << '\n';
}
}
获取局域网ARP表(IP映射MAC地址)
void GetARPIpTableTable() {
PMIB_IPNETTABLE pIpNetTable;
ULONG ulOutBufLen = 0;
DWORD dwRetVal = 0;
// 设置控制台输出为 Unicode
SetConsoleOutputCP(CP_UTF8);
// 第一次调用以获取所需缓冲区大小
dwRetVal = GetIpNetTable(NULL, &ulOutBufLen, 0);
if (dwRetVal != ERROR_INSUFFICIENT_BUFFER) {
std::cerr << "GetIpNetTable 失败,错误代码: " << dwRetVal << '\n';
return;
}
// 分配缓冲区
pIpNetTable = (MIB_IPNETTABLE*)malloc(ulOutBufLen);
if (pIpNetTable == NULL) {
std::cerr << "内存分配错误" << std::endl;
return;
}
// 获取 ARP 表
dwRetVal = GetIpNetTable(pIpNetTable, &ulOutBufLen, 0);
if (dwRetVal == NO_ERROR) {
std::cout << "ARP表条目数: " << pIpNetTable->dwNumEntries << '\n';
for (DWORD i = 0; i < pIpNetTable->dwNumEntries; i++)
{
std::cout
<< "IP 地址: "
<< std::to_string(pIpNetTable->table[i].dwAddr & 0xFF) << "."
<< std::to_string((pIpNetTable->table[i].dwAddr >> 8) & 0xFF) << "."
<< std::to_string((pIpNetTable->table[i].dwAddr >> 16) & 0xFF) << "."
<< std::to_string((pIpNetTable->table[i].dwAddr >> 24) & 0xFF) << " "
<< "MAC 地址: "
<< std::hex
<< std::uppercase
<< (int)pIpNetTable->table[i].bPhysAddr[0] << "-"
<< (int)pIpNetTable->table[i].bPhysAddr[1] << "-"
<< (int)pIpNetTable->table[i].bPhysAddr[2] << "-"
<< (int)pIpNetTable->table[i].bPhysAddr[3] << "-"
<< (int)pIpNetTable->table[i].bPhysAddr[4] << "-"
<< (int)pIpNetTable->table[i].bPhysAddr[5] << "\t"
<< std::dec
<< " 接口索引: " << pIpNetTable->table[i].dwIndex << "\t"
<< " 类型: " << pIpNetTable->table[i].dwType << "\t"
<< '\n';
}
}
else {
std::cerr << "GetIpNetTable 失败,错误代码: " << dwRetVal << '\n';
}
// 释放分配的内存
free(pIpNetTable);
}
创建单播网卡地址,会创建多条路由表.路由表0.0.0.0的是默认路由. 一般的处理方式是,创建虚拟网卡,然后添加路由表,路由表会将流量转发到对应的虚拟网卡.这时候,就可以收发数据了.至于从收到的数据,再次转发到其他路由上,这个我没实现.我觉得应该属于前置路由表的功能.可能还有其他方式可以重定向数据流量到其他适配器.