基於MFC和winpcap的簡單路由器實現

關鍵部分代碼如下。

// 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);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章