windows编程:自定义过滤转发自定义协议

352 阅读15分钟

命令行操作windows路由表

  1. 在windows系统中,查看路由表
route print

接口列列表:

image.png

  1. 添加路由表
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 是网卡索引(可省略)
  1. 删除路由表
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 模型分为七层,每层都有其特定的功能:

  1. 物理层(Physical Layer)

    • 负责物理连接的建立和维护,传输原始比特流(0和1)。
    • 示例:电缆、网卡、信号传输等。
  2. 数据链路层(Data Link Layer)

    • 提供节点间的数据传输,负责错误检测和纠正。
    • 示例:以太网、Wi-Fi、PPP(点对点协议)。
  3. 网络层(Network Layer)

    • 负责数据包的路由和转发,提供逻辑地址(如 IP 地址)。
    • 示例:IP(IPv4、IPv6)、ICMP(互联网控制消息协议)。
  4. 传输层(Transport Layer)

    • 负责端到端的通信,提供可靠性和流量控制。
    • 示例:TCP(传输控制协议)、UDP(用户数据报协议)。
  5. 会话层(Session Layer)

    • 管理会话的建立、维护和终止,负责数据交换的同步。
    • 示例:NetBIOS、RPC(远程过程调用)。
  6. 表示层(Presentation Layer)

    • 负责数据格式的转换和加密,确保应用层可以理解数据。
    • 示例:JPEG、ASCII、SSL/TLS。
  7. 应用层(Application Layer)

    • 用户直接交互的层,提供应用程序服务。
    • 示例:HTTP、FTP、SMTP、DNS。

2. TCP/IP 模型

TCP/IP 模型是互联网的基础,通常分为四层:

应用层
  |
----------------
| TCP/UDP       |  <- 传输层
----------------
| IP            |  <- 网络层(IP数据包)
----------------
| Ethernet      |  <- 数据链路层
----------------
  1. 网络接口层(Network Interface Layer)

    • 对应于 OSI 模型的物理层和数据链路层,负责物理传输和数据帧的处理。
  2. 互联网层(Internet Layer)

    • 对应于 OSI 模型的网络层,负责数据包的路由和寻址。
    • 示例:IP、ICMP。
  3. 传输层(Transport Layer)

    • 对应于 OSI 模型的传输层,负责端到端的通信。
    • 示例:TCP、UDP。
  4. 应用层(Application Layer)

    • 对应于 OSI 模型的会话层、表示层和应用层,提供应用程序服务。
    • 示例:HTTP、FTP、SMTP、DNS。

根据TCP/IP 四层网络架构协议,我们对流量的转发,只需要处理以太网帧这一层即可,上层的IP数据包(网络层),TCP/UDP(传输层) 以及应用层的所有数据均能进行转发.

TCP/IP 4层数据结构代码解析

数据结构示意图(ICMP与TCP UDP 并列):

image.png

包含关系如下: 以太网帧 -> 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的是默认路由. 一般的处理方式是,创建虚拟网卡,然后添加路由表,路由表会将流量转发到对应的虚拟网卡.这时候,就可以收发数据了.至于从收到的数据,再次转发到其他路由上,这个我没实现.我觉得应该属于前置路由表的功能.可能还有其他方式可以重定向数据流量到其他适配器.