關鍵部分代碼如下。
// VCRouterDlg.cpp : 實現文件
//
#include "stdafx.h"
#include "VCRouter.h"
#include "VCRouterDlg.h"
#include "afxdialogex.h"
#include "pcap.h"
#include <stdlib.h>
#include <string.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
#define MAX_IF 5 // 最大接口數目
#pragma pack(1)
typedef struct FrameHeader_t { // 幀首部
UCHAR DesMAC[6]; // 目的地址
UCHAR SrcMAC[6]; // 源地址
USHORT FrameType; // 幀類型
} FrameHeader_t;
typedef struct ARPFrame_t { // ARP 幀
FrameHeader_t FrameHeader; // 幀首部
WORD HardwareType; // 硬件類型
WORD ProtocolType; // 協議類型
BYTE HLen; // 硬件地址長度
BYTE PLen; // 協議地址長度
WORD Operation; // 操作值
UCHAR SendHa[6]; // 源MAC 地址
ULONG SendIP; // 源IP 地址
UCHAR RecvHa[6]; // 目的MAC 地址
ULONG RecvIP; // 目的IP 地址
} ARPFrame_t;
typedef struct IPHeader_t { // IP 首部
BYTE Ver_HLen; // 版本+頭部長度
BYTE TOS; // 服務類型
WORD TotalLen; // 總長度
WORD ID; // 標識
WORD Flag_Segment; // 標誌+片偏移
BYTE TTL; // 生存時間
BYTE Protocol; // 協議
WORD Checksum; // 頭部校驗和
ULONG SrcIP; // 源IP 地址
ULONG DstIP; // 目的IP 地址
} IPHeader_t;
typedef struct ICMPHeader_t { // ICMP 首部
BYTE Type; // 類型
BYTE Code; // 代碼
WORD Checksum; // 校驗和
WORD Id; // 標識
WORD Sequence; // 序列號
} ICMPHeader_t;
typedef struct IPFrame_t { // IP 幀
FrameHeader_t FrameHeader; // 幀首部
IPHeader_t IPHeader; // IP 首部
} IPFrame_t;
typedef struct ip_t { // 網絡地址
ULONG IPAddr; // IP 地址
ULONG IPMask; // 子網掩碼
} ip_t;
typedef struct IfInfo_t { // 接口信息
CString DeviceName; // 設備名
CString Description; // 設備描述
UCHAR MACAddr[6]; // MAC 地址
CArray <ip_t, ip_t&> ip; // IP 地址列表
pcap_t *adhandle; // pcap 句柄
} IfInfo_t;
typedef struct SendPacket_t { // 發送數據包結構
int len; // 長度
BYTE PktData[2000];// 數據緩存
ULONG TargetIP; // 目的IP 地址
UINT_PTR n_mTimer; // 定時器
UINT IfNo; // 接口序號
} SendPacket_t;
typedef struct RouteTable_t { // 路由表結構
ULONG Mask; // 子網掩碼
ULONG DstIP; // 目的地址
ULONG NextHop; // 下一跳步
UINT IfNo; // 接口序號
} RouteTable_t;
typedef struct IP_MAC_t { // IP-MAC 地址映射結構
ULONG IPAddr; // IP 地址
UCHAR MACAddr[6]; // MAC 地址
} IP_MAC_t;
// 全局變量
/******************************************************************
*******/
IfInfo_t IfInfo[MAX_IF]; // 接口信息數組
int IfCount; // 接口個數
UINT_PTR TimerCount; // 定時器個數
CList <SendPacket_t, SendPacket_t&> SP; // 發送數據包緩存隊列
CList <IP_MAC_t, IP_MAC_t&> IP_MAC; // IP-MAC 地址映射列表
CList <RouteTable_t, RouteTable_t&> RouteTable; // 路由表
//CMyRouterDlg *pDlg; // 對話框指針
CVCRouterDlg *pDlg;
CMutex mMutex(0, 0, 0); // 互斥
/******************************************************************
*******/
// 全局函數
/******************************************************************
*******/
// IP 地址轉換
CString IPntoa(ULONG nIPAddr);
// MAC 地址轉換
CString MACntoa(UCHAR *nMACAddr);
// MAC 地址比較
bool cmpMAC(UCHAR *MAC1, UCHAR *MAC2);
// MAC 地址複製
void cpyMAC(UCHAR *MAC1, UCHAR *MAC2);
// MAC 地址設置
void setMAC(UCHAR *MAC, UCHAR ch);
// IP 地址查詢
bool IPLookup(ULONG ipaddr, UCHAR *p);
// 數據包捕獲線程
UINT Capture(PVOID pParam);
// 獲取本地接口MAC 地址線程
UINT CaptureLocalARP(PVOID pParam);
// 發送ARP 請求
void ARPRequest(pcap_t *adhandle, UCHAR *srcMAC, ULONG srcIP, ULONG targetIP);
// 查詢路由表
DWORD RouteLookup(UINT &ifNO, DWORD desIP, CList <RouteTable_t,
RouteTable_t&> *routeTable);
// 處理ARP 數據包
void ARPPacketProc(struct pcap_pkthdr *header, const u_char *pkt_data);
// 處理IP 數據包
void IPPacketProc(IfInfo_t *pIfInfo, struct pcap_pkthdr *header, const u_char
*pkt_data);
// 處理ICMP 數據包
void ICMPPacketProc(IfInfo_t *pIfInfo, BYTE type, BYTE code, const u_char
*pkt_data);
// 檢查IP 數據包頭部校驗和是否正確
int IsChecksumRight(char * buffer);
// 計算校驗和
unsigned short ChecksumCompute(unsigned short *buffer, int size);
//下一部分
// 獲取本地接口MAC 地址線程
UINT CaptureLocalARP(PVOID pParam)
{
int res;
struct pcap_pkthdr *header;
const u_char *pkt_data;
IfInfo_t *pIfInfo;
ARPFrame_t *ARPFrame;
CString DisplayStr;
pIfInfo = (IfInfo_t *)pParam;
while (true)
{
Sleep(50);
res = pcap_next_ex(pIfInfo->adhandle, &header, &pkt_data);
// 超時
if (res == 0)
continue;
if (res > 0)
{
ARPFrame = (ARPFrame_t *)(pkt_data);
// 得到本接口的MAC 地址
if ((ARPFrame->FrameHeader.FrameType == htons(0x0806))
&& (ARPFrame->Operation == htons(0x0002))
&& (ARPFrame->SendIP == pIfInfo->ip[0].IPAddr))
{
cpyMAC(pIfInfo->MACAddr, ARPFrame->SendHa);
return 0;
}
}
}
}
void setMAC(UCHAR *MAC, UCHAR ch)
{
for (int i = 0; i < 6; i++)
{
MAC[i] = ch;
}
return;
}
// 發送ARP 請求
void ARPRequest(pcap_t *adhandle, UCHAR *srcMAC, ULONG srcIP, ULONG
targetIP)
{
ARPFrame_t ARPFrame;
int i;
for (i = 0; i < 6; i++)
{
ARPFrame.FrameHeader.DesMAC[i] = 255;
ARPFrame.FrameHeader.SrcMAC[i] = srcMAC[i];
ARPFrame.SendHa[i] = srcMAC[i];
ARPFrame.RecvHa[i] = 0;
}
ARPFrame.FrameHeader.FrameType = htons(0x0806);
ARPFrame.HardwareType = htons(0x0001);
ARPFrame.ProtocolType = htons(0x0800);
ARPFrame.HLen = 6;
ARPFrame.PLen = 4;
ARPFrame.Operation = htons(0x0001);
ARPFrame.SendIP = srcIP;
ARPFrame.RecvIP = targetIP;
pcap_sendpacket(adhandle, (u_char *)&ARPFrame, sizeof(ARPFrame_t));
}
void cpyMAC(UCHAR *MAC1, UCHAR *MAC2)
{
for (int i = 0; i < 6; i++)
{
MAC1[i] = MAC2[i];
}
}
// 比較兩個MAC 地址是否相同
bool cmpMAC(UCHAR *MAC1, UCHAR *MAC2)
{
for (int i = 0; i < 6; i++)
{
if (MAC1[i] == MAC2[i])
{
continue;
}
else
{
return false;
}
}
return true;
}
// 把IP 地址轉換成點分十進制形式
CString IPntoa(ULONG nIPAddr)
{
char strbuf[50];
u_char *p;
CString str;
p = (u_char *)&nIPAddr;
sprintf_s(strbuf, "%03d.%03d.%03d.%03d", p[0], p[1], p[2], p[3]);
str = strbuf;
return str;
}
// 把MAC 地址轉換成“%02X:%02X:%02X:%02X:%02X:%02X”的格式
CString MACntoa(UCHAR *nMACAddr)
{
char strbuf[50];
CString str;
sprintf_s(strbuf, "%02X:%02X:%02X:%02X:%02X:%02X", nMACAddr[0],
nMACAddr[1],
nMACAddr[2], nMACAddr[3], nMACAddr[4], nMACAddr[5]);
str = strbuf;
return str;
}
// 數據包捕獲線程
UINT Capture(PVOID pParam)
{
int res;
IfInfo_t *pIfInfo;
struct pcap_pkthdr *header;
const u_char *pkt_data;
pIfInfo = (IfInfo_t *)pParam;
// 開始正式接收並處理幀
while (true)
{
res = pcap_next_ex(pIfInfo->adhandle, &header, &pkt_data);
if (res == 1)
{
FrameHeader_t *fh;
fh = (FrameHeader_t *)pkt_data;
switch (ntohs(fh->FrameType))
{
case 0x0806:
ARPFrame_t *ARPf;
ARPf = (ARPFrame_t *)pkt_data;
//TRACE1(" 收到 ARP 包 源 IP 爲: %d\n", ARPf->SendIP);
// ARP 包,轉到ARP 包處理函數
ARPPacketProc(header, pkt_data);
break;
case 0x0800:
IPFrame_t *IPf;
IPf = (IPFrame_t*)pkt_data;
//TRACE1(" 收 到 IP 包 源IP 爲:%d\n",IPf->IPHeader.SrcIP );
// IP 包,轉到IP 包處理函數
IPPacketProc(pIfInfo, header, pkt_data);
break;
default:
break;
}
}
else if (res == 0) // 超時
{
continue;
}
else
{
AfxMessageBox(_T("pcap_next_ex 函數出錯!"));
}
}
return 0;
}
// 處理ARP 數據包
void ARPPacketProc(struct pcap_pkthdr *header, const u_char *pkt_data)
{
bool flag;
ARPFrame_t ARPf;
IPFrame_t *IPf;
SendPacket_t sPacket;
POSITION pos, CurrentPos;
IP_MAC_t ip_mac;
UCHAR macAddr[6];
ARPf = *(ARPFrame_t *)pkt_data;
if (ARPf.Operation == ntohs(0x0002))
{
pDlg->Logger.InsertString(-1, _T("收到ARP響應包"));
pDlg->Logger.InsertString(-1, (_T(" ARP ") + (IPntoa(ARPf.SendIP)) + _T(" -- ")
+ MACntoa(ARPf.SendHa)));
// IP -MAC 地址映射表中已經存在該對應關係
if (IPLookup(ARPf.SendIP, macAddr))
{
pDlg->Logger.InsertString(-1, _T(" 該對應關係已經存在於 IP - MAC 地址映射表中"));
return;
}
else
{
ip_mac.IPAddr = ARPf.SendIP;
memcpy(ip_mac.MACAddr, ARPf.SendHa, 6);
// 將IP-MAC 映射關係存入表中
IP_MAC.AddHead(ip_mac);
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T(" 將該對應關係存入 IP -MAC 地址映射表中"));
}
mMutex.Lock(INFINITE);
do{
// 查看是否能轉發緩存中的IP 數據報
flag = false;
// 沒有需要處理的內容
if (SP.IsEmpty())
{
break;
}
// 遍歷轉發緩存區
pos = SP.GetHeadPosition();
for (int i = 0; i < SP.GetCount(); i++)
{
CurrentPos = pos;
sPacket = SP.GetNext(pos);
if (sPacket.TargetIP == ARPf.SendIP)
{
IPf = (IPFrame_t *)sPacket.PktData;
cpyMAC(IPf->FrameHeader.DesMAC, ARPf.SendHa);
for (int t = 0; t < 6; t++)
{
IPf->FrameHeader.SrcMAC[t] =
IfInfo[sPacket.IfNo].MACAddr[t];
}
// 發送IP 數據包
pcap_sendpacket(IfInfo[sPacket.IfNo].adhandle, (u_char
*)sPacket.PktData, sPacket.len);
SP.RemoveAt(CurrentPos);
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T(" 轉發緩存區中目的地址是該MAC 地址的IP 數據包"));
pDlg->Logger.InsertString(-1, (_T(" 發送 IP 數據包:") + IPntoa(IPf->IPHeader.SrcIP)
+ "->"
+ IPntoa(IPf->IPHeader.DstIP) + " " +
MACntoa(IPf->FrameHeader.SrcMAC)
+ "->" + MACntoa(IPf->FrameHeader.DesMAC)));
flag = true;
break;
}
}
} while (flag);
mMutex.Unlock();
}
}
// 查詢IP-MAC 映射表
bool IPLookup(ULONG ipaddr, UCHAR *p)
{
IP_MAC_t ip_mac;
POSITION pos;
if (IP_MAC.IsEmpty()) return false;
pos = IP_MAC.GetHeadPosition();
for (int i = 0; i < IP_MAC.GetCount(); i++)
{
ip_mac = IP_MAC.GetNext(pos);
if (ipaddr == ip_mac.IPAddr)
{
for (int j = 0; j < 6; j++)
{
p[j] = ip_mac.MACAddr[j];
}
return true;
}
}
return false;
}
// 處理IP 數據包
void IPPacketProc(IfInfo_t *pIfInfo, struct pcap_pkthdr *header, const u_char
*pkt_data)
{
IPFrame_t *IPf;
SendPacket_t sPacket;
IPf = (IPFrame_t *)pkt_data;
pDlg->Logger.InsertString(-1, (_T(" 收 到 IP 數 據 包 :") +
IPntoa(IPf->IPHeader.SrcIP) + "->"
+ IPntoa(IPf->IPHeader.DstIP)));
// ICMP 超時
if (IPf->IPHeader.TTL <= 0)
{
ICMPPacketProc(pIfInfo, 11, 0, pkt_data);
return;
}
IPHeader_t *IpHeader = &(IPf->IPHeader);
// ICMP 差錯
if (IsChecksumRight((char *)IpHeader) == 0)
{
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T(" IP 數據包包頭校驗和錯誤,丟棄數據包"));
return;
}
// 路由查詢
DWORD nextHop; // 經過路由選擇算法得到的下一站目的IP 地址
UINT ifNo; // 下一跳的接口序號
// 路由查詢
if ((nextHop = RouteLookup(ifNo, IPf->IPHeader.DstIP, &RouteTable)) == -1)
{
// ICMP 目的不可達
ICMPPacketProc(pIfInfo, 3, 0, pkt_data);
return;
}
else
{
sPacket.IfNo = ifNo;
sPacket.TargetIP = nextHop;
cpyMAC(IPf->FrameHeader.SrcMAC, IfInfo[sPacket.IfNo].MACAddr);
// TTL 減1
IPf->IPHeader.TTL -= 1;
unsigned short check_buff[sizeof(IPHeader_t)];
// 設IP 頭中的校驗和爲0
IPf->IPHeader.Checksum = 0;
memset(check_buff, 0, sizeof(IPHeader_t));
IPHeader_t * ip_header = &(IPf->IPHeader);
memcpy(check_buff, ip_header, sizeof(IPHeader_t));
// 計算IP 頭部校驗和
IPf->IPHeader.Checksum = ChecksumCompute(check_buff,
sizeof(IPHeader_t));
// IP-MAC 地址映射表中存在該映射關係
if (IPLookup(sPacket.TargetIP, IPf->FrameHeader.DesMAC))
{
memcpy(sPacket.PktData, pkt_data, header->len);
sPacket.len = header->len;
if (pcap_sendpacket(IfInfo[sPacket.IfNo].adhandle, (u_char *)
sPacket.PktData, sPacket.len) != 0)
{
// 錯誤處理
AfxMessageBox(_T("發送IP 數據包時出錯!"));
return;
}
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T(" 轉發IP 數據包:"));
pDlg->Logger.InsertString(-1, (_T(" ") + IPntoa(IPf->IPHeader.SrcIP)
+ "->"
+ IPntoa(IPf->IPHeader.DstIP) + _T(" ") +
MACntoa(IPf->FrameHeader.SrcMAC)
+ "->" + MACntoa(IPf->FrameHeader.DesMAC)));
}
// IP-MAC 地址映射表中不存在該映射關係
else
{
if (SP.GetCount() < 65530) // 存入緩存隊列
{
sPacket.len = header->len;
// 將需要轉發的數據報存入緩存區
memcpy(sPacket.PktData, pkt_data, header->len);
// 在某一時刻只允許一個線程維護鏈表
mMutex.Lock(INFINITE);
sPacket.n_mTimer = TimerCount;
if (TimerCount++ > 65533)
{
TimerCount = 1;
}
pDlg->SetTimer(sPacket.n_mTimer, 10000, NULL);
SP.AddTail(sPacket);
mMutex.Unlock();
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T(" 缺少目的MAC 地址,將IP數據包存入轉發緩衝區"));
pDlg->Logger.InsertString(-1, (_T(" 存入轉發緩衝區的數據包爲:") + IPntoa(IPf->IPHeader.SrcIP)
+ "->" + IPntoa(IPf->IPHeader.DstIP) + " " +
MACntoa(IPf->FrameHeader.SrcMAC)
+ "->xx:xx:xx:xx:xx:xx"));
pDlg->Logger.InsertString(-1, _T(" 發送ARP 請求"));
// 發送ARP 請求
ARPRequest(IfInfo[sPacket.IfNo].adhandle,
IfInfo[sPacket.IfNo].MACAddr,
IfInfo[sPacket.IfNo].ip[1].IPAddr, sPacket.TargetIP);
}
else // 如緩存隊列太長,拋棄該報
{
// 日誌輸出信息
pDlg->Logger.InsertString(-1, _T("轉發緩衝區溢出,丟棄IP數據包"));
pDlg->Logger.InsertString(-1, (_T(" 丟棄的IP 數據包爲:") +
IPntoa(IPf->IPHeader.SrcIP) + "->"
+ IPntoa(IPf->IPHeader.DstIP) + " " +
MACntoa(IPf->FrameHeader.SrcMAC)
+ "->xx:xx:xx:xx:xx:xx"));
}
}
}
}
// 判斷IP 數據包頭部校驗和是否正確
int IsChecksumRight(char * buffer)
{
// 獲得IP 頭內容
IPHeader_t * ip_header = (IPHeader_t *)buffer;
// 備份原來的校驗和
unsigned short checksumBuf = ip_header->Checksum;
unsigned short check_buff[sizeof(IPHeader_t)];
// 設IP 頭中的校驗和爲0
ip_header->Checksum = 0;
memset(check_buff, 0, sizeof(IPHeader_t));
memcpy(check_buff, ip_header, sizeof(IPHeader_t));
// 計算IP 頭部校驗和
ip_header->Checksum = ChecksumCompute(check_buff, sizeof(IPHeader_t));
// 與備份的校驗和進行比較
if (ip_header->Checksum == checksumBuf)
{
return 1;
}
else
{
return 0;
}
}
// 查詢路由表
DWORD RouteLookup(UINT &ifNO, DWORD desIP, CList <RouteTable_t,
RouteTable_t&> *routeTable)
{
// desIP 爲網絡序
DWORD MaxMask = 0; // 獲得最大的子網掩碼的地址,沒有獲得時初始爲 - 1
int Index = -1; // 獲得最大的子網掩碼的地址對應的路由表 索引,以便獲得下一站路由器的地址
POSITION pos;
RouteTable_t rt;
DWORD tmp;
pos = routeTable->GetHeadPosition();
for (int i = 0; i < routeTable->GetCount(); i++)
{
rt = routeTable->GetNext(pos);
if ((desIP & rt.Mask) == rt.DstIP)
{
Index = i;
if (rt.Mask >= MaxMask)
{
ifNO = rt.IfNo;
if (rt.NextHop == 0) // 直接投遞
{
tmp = desIP;
}
else
{
tmp = rt.NextHop;
}
}
}
}
if (Index == -1) // 目的不可達
{
return -1;
}
else // 找到了下一跳地址
{
return tmp;
}
}
// 發送ICMP 數據包
void ICMPPacketProc(IfInfo_t *pIfInfo, BYTE type, BYTE code, const u_char
*pkt_data)
{
u_char * ICMPBuf = new u_char[70];
// 填充幀首部
memcpy(((FrameHeader_t *)ICMPBuf)->DesMAC, ((FrameHeader_t
*)pkt_data)->SrcMAC, 6);
memcpy(((FrameHeader_t *)ICMPBuf)->SrcMAC, ((FrameHeader_t
*)pkt_data)->DesMAC, 6);
((FrameHeader_t *)ICMPBuf)->FrameType = htons(0x0800);
// 填充IP 首部
((IPHeader_t *)(ICMPBuf + 14))->Ver_HLen = ((IPHeader_t
*)(pkt_data + 14))->Ver_HLen;
((IPHeader_t *)(ICMPBuf + 14))->TOS = ((IPHeader_t
*)(pkt_data + 14))->TOS;
((IPHeader_t *)(ICMPBuf + 14))->TotalLen = htons(56);
((IPHeader_t *)(ICMPBuf + 14))->ID = ((IPHeader_t *)(pkt_data + 14))->ID;
((IPHeader_t *)(ICMPBuf + 14))->Flag_Segment = ((IPHeader_t
*)(pkt_data + 14))->Flag_Segment;
((IPHeader_t *)(ICMPBuf + 14))->TTL = 64;
((IPHeader_t *)(ICMPBuf + 14))->Protocol = 1;
((IPHeader_t *)(ICMPBuf + 14))->SrcIP = ((IPHeader_t
*)(pkt_data + 14))->DstIP;
((IPHeader_t *)(ICMPBuf + 14))->DstIP = ((IPHeader_t
*)(pkt_data + 14))->SrcIP;
((IPHeader_t *)(ICMPBuf + 14))->Checksum =
htons(ChecksumCompute((unsigned short *)(ICMPBuf + 14), 20));
// 填充ICMP 首部
((ICMPHeader_t *)(ICMPBuf + 34))->Type = type;
((ICMPHeader_t *)(ICMPBuf + 34))->Code = code;
((ICMPHeader_t *)(ICMPBuf + 34))->Id = 0;
((ICMPHeader_t *)(ICMPBuf + 34))->Sequence = 0;
((ICMPHeader_t *)(ICMPBuf + 34))->Checksum =
htons(ChecksumCompute((unsigned short *)(ICMPBuf + 34), 8));
// 填充數據
memcpy((u_char *)(ICMPBuf + 42), (IPHeader_t *)(pkt_data + 14), 20);
memcpy((u_char *)(ICMPBuf + 62), (u_char *)(pkt_data + 34), 8);
// 發送數據包
pcap_sendpacket(pIfInfo->adhandle, (u_char *)ICMPBuf, 70);
// 日誌輸出信息
if (type == 11)
{
pDlg->Logger.InsertString(-1, _T(" 發送ICMP 超時數據包:"));
}
if (type == 3)
{
pDlg->Logger.InsertString(-1, _T(" 發送ICMP 目的不可達數據包:"));
}
pDlg->Logger.InsertString(-1, (_T(" ICMP ->") + IPntoa(((IPHeader_t
*)(ICMPBuf + 14))->DstIP)
+ "-" + MACntoa(((FrameHeader_t *)ICMPBuf)->DesMAC)));
delete[] ICMPBuf;
}
// 計算校驗和
unsigned short ChecksumCompute(unsigned short * buffer, int size)
{
// 32 位,延遲進位
unsigned long cksum = 0;
while (size > 1)
{
cksum += *buffer++;
// 16 位相加
size -= sizeof(unsigned short);
}
if (size)
{
// 最後可能有單獨8 位
cksum += *(unsigned char *)buffer;
}
// 將高 16 位進位加至低16 位
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
// 取反
return (unsigned short)(~cksum);
}
// 用於應用程序“關於”菜單項的 CAboutDlg 對話框
class CAboutDlg : public CDialogEx
{
public:
CAboutDlg();
// 對話框數據
enum { IDD = IDD_ABOUTBOX };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
// 實現
protected:
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialogEx(CAboutDlg::IDD)
{
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()
// CVCRouterDlg 對話框
CVCRouterDlg::CVCRouterDlg(CWnd* pParent /*=NULL*/)
: CDialogEx(CVCRouterDlg::IDD, pParent)
{
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CVCRouterDlg::DoDataExchange(CDataExchange* pDX)
{
CDialogEx::DoDataExchange(pDX);
DDX_Control(pDX, LOGGER_LIST, Logger);
DDX_Control(pDX, ROUTER_LIST, m_RouteTable);
DDX_Control(pDX, IDC_NEXTHOP, m_NextHop);
DDX_Control(pDX, IDC_NETMASK, m_Mask);
DDX_Control(pDX, IDC_IPADDRESS, m_Destination);
}
BEGIN_MESSAGE_MAP(CVCRouterDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(ONSTART_BUTTON, &CVCRouterDlg::OnStartClickedButton)
ON_BN_CLICKED(ONSTOP_BUTTON, &CVCRouterDlg::OnBnClickedButton)
ON_BN_CLICKED(ADD_ROUTER_BUTTON, &CVCRouterDlg::OnAddRouterButton)
ON_BN_CLICKED(DELETE_ROUTER_BUTTON, &CVCRouterDlg::OnDeleteRouterButton)
ON_WM_DESTROY()
ON_WM_TIMER()
END_MESSAGE_MAP()
// CVCRouterDlg 消息處理程序
BOOL CVCRouterDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
CVCRouterApp* pApp = (CVCRouterApp*)AfxGetApp();
pDlg = (CVCRouterDlg*)pApp->m_pMainWnd;
// 將“關於...”菜單項添加到系統菜單中。
// IDM_ABOUTBOX 必須在系統命令範圍內。
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
BOOL bNameValid;
CString strAboutMenu;
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
ASSERT(bNameValid);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
// 設置此對話框的圖標。 當應用程序主窗口不是對話框時,框架將自動
// 執行此操作
SetIcon(m_hIcon, TRUE); // 設置大圖標
SetIcon(m_hIcon, FALSE); // 設置小圖標
// TODO: 在此添加額外的初始化代碼
return TRUE; // 除非將焦點設置到控件,否則返回 TRUE
}
void CVCRouterDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialogEx::OnSysCommand(nID, lParam);
}
}
// 如果向對話框添加最小化按鈕,則需要下面的代碼
// 來繪製該圖標。 對於使用文檔/視圖模型的 MFC 應用程序,
// 這將由框架自動完成。
void CVCRouterDlg::OnPaint()
{
if (IsIconic())
{
CPaintDC dc(this); // 用於繪製的設備上下文
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
// 使圖標在工作區矩形中居中
int cxIcon = GetSystemMetrics(SM_CXICON);
int cyIcon = GetSystemMetrics(SM_CYICON);
CRect rect;
GetClientRect(&rect);
int x = (rect.Width() - cxIcon + 1) / 2;
int y = (rect.Height() - cyIcon + 1) / 2;
// 繪製圖標
dc.DrawIcon(x, y, m_hIcon);
}
else
{
CDialogEx::OnPaint();
}
}
//當用戶拖動最小化窗口時系統調用此函數取得光標
//顯示。
HCURSOR CVCRouterDlg::OnQueryDragIcon()
{
return static_cast<HCURSOR>(m_hIcon);
}
void CVCRouterDlg::OnStartClickedButton()
{
// TODO: 在此添加控件通知處理程序代碼
pcap_if_t *alldevs, *d;
pcap_addr_t *a;
struct bpf_program fcode;
char errbuf[PCAP_ERRBUF_SIZE], strbuf[1000];
int i, j, k;
ip_t ipaddr;
UCHAR srcMAC[6];
ULONG srcIP;
SetTimer(3999, 10000, 0);
// 獲得本機的設備列表
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL /* 無 需 認 證 */,
&alldevs, errbuf) == -1)
{
// 錯誤,返回錯誤信息
sprintf_s(strbuf, "pcap_findalldevs_ex 錯誤: %s", errbuf);
MessageBox((LPCTSTR)strbuf);
PostMessage(WM_QUIT, 0, 0);
}
i = 0;
j = 0;
k = 0;
// 獲取 IP 地址信息
for (d = alldevs; d != NULL; d = d->next)
{
if (d->addresses != NULL) // 排除集成 modem 的影響(沒有 IP 地址)
{
// 得到一個有效的接口和其 IP 地址列表
IfInfo[i].DeviceName = d->name;
IfInfo[i].Description = d->description;
for (a = d->addresses; a; a = a->next)
{
if (a->addr->sa_family == AF_INET)
{
ipaddr.IPAddr = (((struct sockaddr_in
*)a->addr)->sin_addr.s_addr);
ipaddr.IPMask = (((struct sockaddr_in
*)a->netmask)->sin_addr.s_addr);
IfInfo[i].ip.Add(ipaddr);
j++;
}
}
if (i == MAX_IF) // 最多處理 MAX_IF 個接口
{
break;
}
else
{
i++;
}
}
}
// 不符合路由器 IP 地址數目要求
if (j < 2)
{
MessageBox("該路由程序要求本地主機至少應具有 2 個 IP 地址");
PostMessage(WM_QUIT, 0, 0);
}
// 保存實際的網卡數
IfCount = i;
// 打開接口
for (i = 0; i < IfCount; i++)
{
if ((IfInfo[i].adhandle = pcap_open(IfInfo[i].DeviceName, // 設備名
65536, // 最大包長度
PCAP_OPENFLAG_PROMISCUOUS,
// 混雜模式
1000, // 超時時間
NULL, // 遠程認證
errbuf // 錯誤緩存
)) == NULL)
{
// 錯誤,顯示錯誤信息
sprintf_s(strbuf, " 接 口 未 能 打 開 。 WinPcap 不 支 持 %s 。 ",
IfInfo[i].DeviceName);
MessageBox((LPCTSTR)strbuf);
PostMessage(WM_QUIT, 0, 0);
}
}
// 開啓數據包捕獲線程,獲取本地接口的 MAC 地址,線程數目爲網卡個數
CWinThread* pthread;
for (i = 0; i < IfCount; i++)
{
pthread = AfxBeginThread(CaptureLocalARP, &IfInfo[i],
THREAD_PRIORITY_NORMAL);
if (!pthread)
{
MessageBox(_T("創建數據包捕獲線程失敗! "));
PostMessage(WM_QUIT, 0, 0);
}
}
// 將列表中網卡硬件地址清 0
for (i = 0; i < IfCount; i++)
{
setMAC(IfInfo[i].MACAddr, 0);
}
// 爲得到真實網卡地址,使用虛假的 MAC 地址和 IP 地址向本機發送 ARP 請求
setMAC(srcMAC, 66); // 設置虛假的 MAC 地址
srcIP = inet_addr("112.112.112.112"); // 設置虛假的 IP 地址
for (i = 0; i < IfCount; i++)
{
ARPRequest(IfInfo[i].adhandle, srcMAC, srcIP, IfInfo[i].ip[0].IPAddr);
}
// 確保所有接口的 MAC 地址完全收到
setMAC(srcMAC, 0);
do {
Sleep(1000);
k = 0;
for (i = 0; i < IfCount; i++)
{
if (!cmpMAC(IfInfo[i].MACAddr, srcMAC))
{
k++;
continue;
}
else
{
break;
}
}
} while (!((j++ > 10) || (k == IfCount)));
if (k != IfCount)
{
MessageBox(_T("至少有一個接口的 MAC 地址沒能得到! "));
PostMessage(WM_QUIT, 0, 0);
}
// 日誌輸出接口信息
for (i = 0; i < IfCount; i++)
{
Logger.InsertString(-1, _T("接口:"));
Logger.InsertString(-1, _T(" 設備名: ") + IfInfo[i].DeviceName);
Logger.InsertString(-1, _T(" 設備描述: ") + IfInfo[i].Description);
Logger.InsertString(-1, (_T("MAC 地址:") + MACntoa(IfInfo[i].MACAddr)));
for (j = 0; j < IfInfo[i].ip.GetSize(); j++)
{
Logger.InsertString(-1, (_T("IP 地址: ") + IPntoa(IfInfo[i].ip[j].IPAddr)));
}
}
// 初始化路由表並顯示
RouteTable_t rt;
for (i = 0; i < IfCount; i++)
{
for (j = 0; j < IfInfo[i].ip.GetSize(); j++)
{
rt.IfNo = i;
rt.DstIP = IfInfo[i].ip[j].IPAddr & IfInfo[i].ip[j].IPMask;
rt.Mask = IfInfo[i].ip[j].IPMask;
rt.NextHop = 0; // 直接投遞
RouteTable.AddTail(rt);
m_RouteTable.InsertString(-1, IPntoa(rt.Mask) + " -- " +
IPntoa(rt.DstIP) + " -- " + IPntoa(rt.NextHop) + " (直接投遞)");
}
}
// 設置過濾規則:僅僅接收 arp 響應幀和需要路由的幀
CString Filter, Filter0, Filter1;
Filter0 = "(";
Filter1 = "(";
for (i = 0; i < IfCount; i++)
{
Filter0 += _T("(ether dst ") + MACntoa(IfInfo[i].MACAddr) + _T(")");
for (j = 0; j < IfInfo[i].ip.GetSize(); j++)
{
Filter1 += _T("(ip dst host ") + IPntoa(IfInfo[i].ip[j].IPAddr) + _T(")");
if (((j == (IfInfo[i].ip.GetSize() - 1))) && (i == (IfCount - 1)))
{
Filter1 += ")";
}
else
{
Filter1 += " or ";
}
}
if (i == (IfCount - 1))
{
Filter0 += ")";
}
else
{
Filter0 += " or ";
}
}
Filter = Filter0 + _T(" and ((arp and (ether[21]=0x2)) or (not") + Filter1 + _T("))");
sprintf_s(strbuf, "%s", Filter);
//TRACE1("filter:%s /n",strbuf);
for (i = 0; i < IfCount; i++)
{
if (pcap_compile(IfInfo[i].adhandle, &fcode, strbuf, 1,
IfInfo[i].ip[0].IPMask) < 0)
{
MessageBox(_T("過濾規則編譯不成功,請檢查書寫的規則語法是否正確! "));
PostMessage(WM_QUIT, 0, 0);
}
if (pcap_setfilter(IfInfo[i].adhandle, &fcode) < 0)
{
MessageBox(_T("設置過濾器錯誤! "));
PostMessage(WM_QUIT, 0, 0);
}
}
// 不再需要該設備列表,釋放之
pcap_freealldevs(alldevs);
TimerCount = 1;
// 開始捕獲數據包
for (i = 0; i < IfCount; i++)
{
pthread = AfxBeginThread(Capture, &IfInfo[i],
THREAD_PRIORITY_NORMAL);
if (!pthread)
{
MessageBox(_T("創建數據包捕獲線程失敗! "));
PostMessage(WM_QUIT, 0, 0);
}
}
}
void CVCRouterDlg::OnBnClickedButton()
{
// TODO: 在此添加控件通知處理程序代碼
SendMessage(WM_CLOSE);
}
void CVCRouterDlg::OnAddRouterButton()
{
// TODO: 在此添加控件通知處理程序代碼
bool flag;
int i, j;
DWORD ipaddr;
RouteTable_t rt;
m_NextHop.GetAddress(ipaddr);
ipaddr = htonl(ipaddr);
// 檢查合法性
flag = false;
for (i = 0; i < IfCount; i++)
{
for (j = 0; j < IfInfo[i].ip.GetSize(); j++)
{
if (((IfInfo[i].ip[j].IPAddr) & (IfInfo[i].ip[j].IPMask)) ==
((IfInfo[i].ip[j].IPMask) & ipaddr))
{
rt.IfNo = i;
// 記錄子網掩碼
m_Mask.GetAddress(ipaddr);
rt.Mask = htonl(ipaddr);
// 記錄目的 IP
m_Destination.GetAddress(ipaddr);
rt.DstIP = htonl(ipaddr);
// 記錄下一跳
m_NextHop.GetAddress(ipaddr);
rt.NextHop = htonl(ipaddr);
// 把該條路由表項添加到路由表
RouteTable.AddTail(rt);
// 在路由表窗口中顯示該路由表項
m_RouteTable.InsertString(-1, IPntoa(rt.Mask) + " -- "
+ IPntoa(rt.DstIP) + " -- " + IPntoa(rt.NextHop));
flag = true;
}
}
}
if (!flag)
{
MessageBox(_T("輸入錯誤,請重新輸入! "));
}
}
void CVCRouterDlg::OnDeleteRouterButton()
{
// TODO: 在此添加控件通知處理程序代碼
int i;
char str[100], ipaddr[20];
ULONG mask, destination, nexthop;
RouteTable_t rt;
POSITION pos, CurrentPos;
str[0] = NULL;
ipaddr[0] = NULL;
if ((i = m_RouteTable.GetCurSel()) == LB_ERR)
{
return;
}
m_RouteTable.GetText(i, (LPTSTR)str);
// 取得子網掩碼選項
strncat_s(ipaddr, str, 15);
mask = inet_addr(ipaddr);
// 取得目的地址選項
ipaddr[0] = 0;
strncat_s(ipaddr, &str[19], 15);
destination = inet_addr(ipaddr);
// 取得下一跳選項
ipaddr[0] = 0;
strncat_s(ipaddr, &str[38], 15);
nexthop = inet_addr(ipaddr);
if (nexthop == 0)
{
MessageBox(_T("直接連接路由,不允許刪除! "));
return;
}
// 把該路由表項從路由表窗口中刪除
m_RouteTable.DeleteString(i);
// 路由表中沒有需要處理的內容,則返回
if (RouteTable.IsEmpty())
{
return;
}
// 遍歷路由表,把需要刪除的路由表項從路由表中刪除
pos = RouteTable.GetHeadPosition();
for (i = 0; i < RouteTable.GetCount(); i++)
{
CurrentPos = pos;
rt = RouteTable.GetNext(pos);
if ((rt.Mask == mask) && (rt.DstIP == destination) && (rt.NextHop ==
nexthop))
{
RouteTable.RemoveAt(CurrentPos);
return;
}
}
}
//????????????
void CVCRouterDlg::OnDestroy()
{
// TODO: Add your control notification handler code here
CDialog::OnDestroy();
// TODO: Add your message handler code here
SP.RemoveAll();
IP_MAC.RemoveAll();
RouteTable.RemoveAll();
for (int i = 0; i < IfCount; i++)
{
IfInfo[i].ip.RemoveAll();
}
}
void CVCRouterDlg::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
SendPacket_t sPacket;
POSITION pos, CurrentPos;
IPFrame_t *IPf;
// 沒有需要處理的內容
if (SP.IsEmpty())
{
return;
}
mMutex.Lock(INFINITE);
// 遍歷轉發緩存區
pos = SP.GetHeadPosition();
for (int i = 0; i < SP.GetCount(); i++)
{
CurrentPos = pos;
sPacket = SP.GetNext(pos);
if (sPacket.n_mTimer == nIDEvent)
{
IPf = (IPFrame_t *)sPacket.PktData;
// 日誌輸出信息
Logger.InsertString(-1, _T("IP 數據報在轉發隊列中等待 10 秒後還未能被轉發"));
Logger.InsertString(-1, (_T(" 定時器中刪除該 IP 數據報: ") +
IPntoa(IPf->IPHeader.SrcIP) + "->"
+ IPntoa(IPf->IPHeader.DstIP) + " " +
MACntoa(IPf->FrameHeader.SrcMAC)
+ _T("->xx:xx:xx:xx:xx:xx")));
KillTimer(sPacket.n_mTimer);
SP.RemoveAt(CurrentPos);
}
}
mMutex.Unlock();
CDialog::OnTimer(nIDEvent);
}