Winpcap网络编程十之Winpcap实战,两台主机通过中间主机通信
注:源碼等等的我不會全然公開的,此篇文章寫出來為大家的網(wǎng)絡(luò)編程或者課程設(shè)計提供一定的思路..
好,本次我們須要完畢的任務(wù)是:
完畢兩臺主機通過中間主機的數(shù)據(jù)通信(網(wǎng)絡(luò)層)
- ?添加基于IP地址的轉(zhuǎn)發(fā)功能
- ?添加網(wǎng)絡(luò)層封裝
事實上最基本的就是基于IP地址的轉(zhuǎn)發(fā)功能,網(wǎng)絡(luò)層的封裝事實上我們在0基礎(chǔ)功能中就已經(jīng)做好了。
首先,實驗的思路是A通過中間主機B向C發(fā)送數(shù)據(jù)。那么B則作為一個路由器,B要監(jiān)聽兩個網(wǎng)卡,一個網(wǎng)卡發(fā)來的數(shù)據(jù)通過還有一個網(wǎng)卡發(fā)出去。
示意圖例如以下:
A--------->B1===B2------------>C
從圖上能夠看出,B主機的兩個網(wǎng)卡數(shù)據(jù)互通,A和B1則處于一個局域網(wǎng)內(nèi),B2和C處于還有一個局域網(wǎng)內(nèi)。
就比方這樣,如今室友A在用有線上網(wǎng),我的電腦B也在用有線上網(wǎng),我們的有線處在同一局域網(wǎng),我的電腦B同一時候散著一個無線網(wǎng),我的手機C又連接到了這個無線上。
那么要實現(xiàn)A到C的數(shù)據(jù)傳送,即模擬室友A要發(fā)送數(shù)據(jù)到我的手機C,那么流程則是這種:
室友A在有線局域網(wǎng)發(fā)送數(shù)據(jù)到我的網(wǎng)卡B1,B1將數(shù)據(jù)通過網(wǎng)卡B2轉(zhuǎn)發(fā)到無線局域網(wǎng),通過無線局域網(wǎng)到達我的手機C。
A的發(fā)送要構(gòu)建一個幀,目的MAC地址為B1,目的IP為C。B則要開啟兩個網(wǎng)卡,B1監(jiān)聽接收數(shù)據(jù),B2網(wǎng)卡則要用ARP協(xié)議掃描所在無線局域網(wǎng)內(nèi)的IP和MAC,B獲取到了A發(fā)來的幀之后,解析它的IP地址和MAC地址,匹配剛才掃描得到的IP和MAC相應(yīng)表,將源MAC換成B2網(wǎng)卡MAC,目的MAC換成C的MAC,IP不變,數(shù)據(jù)data不變。構(gòu)建新幀之后發(fā)送出去。
好啦,思路大體就是這樣。
須要三個程序,一個是發(fā)送,一個路由,一個接收。所以一共三個程序要同一時候執(zhí)行起來執(zhí)行。
以上是我的大體思路,如有錯誤,還請指正。現(xiàn)已用代碼實現(xiàn)完成。
代碼暫不公開,僅僅提供部分重點代碼解析:
一、發(fā)送端
事實上發(fā)送端和0基礎(chǔ)功能的發(fā)送差點兒相同
個人編寫的交互流程例如以下:
IP地址:121.250.216.221 MAC地址:3c970e4b56d6con:127------------------------------------------- IP地址:121.250.216.227 MAC地址:089e01b948f4con:128------------------------------------------- IP地址:121.250.216.228 MAC地址:10bf48705aeecon:129獲取MAC地址完成,請輸入你要發(fā)送對方的IP地址: 192.168.1.3 請輸入你要發(fā)送的內(nèi)容: im cqc 要發(fā)送的內(nèi)容:im cqc詳細代碼不再解析,同上一篇0基礎(chǔ)功能。
二、路由端
首先要開啟兩個網(wǎng)卡,聲明兩個網(wǎng)卡對象和處理器
pcap_if_t *d,*d2; //選中的網(wǎng)絡(luò)適配器 pcap_t *adhandle,*adhandle2; //捕捉實例,是pcap_open返回的對象,adhandle是用來發(fā)送數(shù)據(jù),adhandle2是用來接收數(shù)據(jù)一個用來接收一個用來發(fā)送,這里定義了adhandle是用來發(fā)送,adhandle2是用來接收數(shù)據(jù)。
?那么打開適配器就在main方法中,提前打開兩個網(wǎng)卡
int num;printf("請輸入你要轉(zhuǎn)發(fā)數(shù)據(jù)的網(wǎng)卡代號:\n");//讓用戶選擇選擇哪個適配器進行轉(zhuǎn)發(fā)scanf_s("%d",&num);//跳轉(zhuǎn)到選中的適配器for(d=alldevs, i=0; i< num-1 ; d=d->next, i++);//執(zhí)行到此處說明用戶的輸入是合法的,找到發(fā)送數(shù)據(jù)網(wǎng)卡if((adhandle = pcap_open(d->name, //設(shè)備名稱65535, //存放數(shù)據(jù)包的內(nèi)容長度PCAP_OPENFLAG_PROMISCUOUS, //混雜模式1000, //超時時間NULL, //遠程驗證errbuf //錯誤緩沖)) == NULL){//打開適配器失敗,打印錯誤并釋放適配器列表fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d->name);// 釋放設(shè)備列表 pcap_freealldevs(alldevs);return -1;}int num2;printf("請輸入你要接收數(shù)據(jù)的網(wǎng)卡代號:");//讓用戶選擇用哪個網(wǎng)卡來收數(shù)據(jù)scanf_s("%d",&num2);//用戶輸入的數(shù)字超出合理范圍//跳轉(zhuǎn)到選中的適配器for(d2=alldevs, i=0; i< num2-1 ; d2=d2->next, i++);//執(zhí)行到此處說明用戶的輸入是合法的if((adhandle2 = pcap_open(d2->name, //設(shè)備名稱65535, //存放數(shù)據(jù)包的內(nèi)容長度PCAP_OPENFLAG_PROMISCUOUS, //混雜模式1000, //超時時間NULL, //遠程驗證errbuf //錯誤緩沖)) == NULL){//打開適配器失敗,打印錯誤并釋放適配器列表fprintf(stderr,"\nUnable to open the adapter. %s is not supported by WinPcap\n", d2->name);接下來用用于發(fā)送的handle處理器來掃描它的局域網(wǎng)IP,獲得局域網(wǎng)內(nèi)的MAC地址,記錄在一個表中,存放IP和MAC的相應(yīng)關(guān)系。
這個表能夠用結(jié)構(gòu)體數(shù)組來保存,比方能夠這樣:
struct ip_mac_list{IpAddress ip;unsigned char mac[6]; };ip_mac_list list[256]; //存儲IP和MAC地址的相應(yīng)表那么以上便是準(zhǔn)備工作,我們完畢了兩個網(wǎng)卡的打開,發(fā)送網(wǎng)卡掃描獲取局域網(wǎng)MAC,接下來便是最重要的監(jiān)聽加轉(zhuǎn)發(fā)。
那么這個怎辦?那就開一個新線程。
讓我們聲明一個新的路由線程。
?
DWORD WINAPI RouteThread(LPVOID lpParameter);那么線程要接收進來什么參數(shù)呢?
首先必需要的是兩個網(wǎng)卡處理器,在main方法中已經(jīng)做好初始化的adhandle和adhandle2,另外還有alldevs,能夠持有這個指針來釋放設(shè)備列表,出現(xiàn)錯誤時釋放資源并退出。
0基礎(chǔ)功能中聲明過了
struct sparam sp;
struct gparam gp;
這兩個就是發(fā)送ARP線程和接收ARP線程中的兩個參數(shù),那么仿照這個功能,我們定義一個新的結(jié)構(gòu)體
struct rparam{pcap_t *adhandle_rec;pcap_t *adhandle_send;pcap_if_t * alldevs; //全部網(wǎng)絡(luò)適配器 };在main方法中把它來初始化賦值
?
rp.adhandle_send = adhandle;rp.adhandle_rec = adhandle2;rp.alldevs = alldevs;當(dāng)做參數(shù)傳入這個線程
?
routethread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RouteThread, &rp,0, NULL);當(dāng)中第四個參數(shù)就是傳遞了這個結(jié)構(gòu)體進去。注意這個語句最好不要直接放在main方法中直接調(diào)用,能夠在所有獲取完MAC地址之后再開啟這個線程。
那么接下來就說一下這個線程都干了些什么,僅僅簡略說一下核心部分。
首先開啟了這個線程之后會一直都在運行,那么就能夠增加
while((res = pcap_next_ex(adhandle2,&header,&pkt_data))>=0)這種while推斷語句來一直監(jiān)聽數(shù)據(jù)包的接收,然后解析數(shù)據(jù)。
?
ethernet = (EthernetHeader *)(pkt_data);for(int i=0;i<6;i++){sou_mac[i] = ethernet->SourMAC[i];}for(int i=0;i<6;i++){des_mac[i] = ethernet->DestMAC[i];}// 獲得IP數(shù)據(jù)包頭部的位置ip = (IpHeader *) (pkt_data +14); //14為以太網(wǎng)幀頭部長度//獲得TCP頭部的位置ip_len = (ip->Version_HLen & 0xf) *4;tcp = (TcpHeader *)((u_char *)ip+ip_len);data = (char *)((u_char *)tcp+20);printf("data:%s\n",data);printf("ip:");printf("%d.%d.%d.%d -> %d.%d.%d.%d\n",ip->SourceAddr.byte1,ip->SourceAddr.byte2,ip->SourceAddr.byte3,ip->SourceAddr.byte4,ip->DestinationAddr.byte1,ip->DestinationAddr.byte2,ip->DestinationAddr.byte3,ip->DestinationAddr.byte4);printf("sou_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", sou_mac[0], sou_mac[1], sou_mac[2],sou_mac[3], sou_mac[4], sou_mac[5]);printf("des_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", des_mac[0], des_mac[1], des_mac[2],des_mac[3], des_mac[4], des_mac[5]);
然后接下來每接收到一個數(shù)據(jù),就進行構(gòu)建新的幀轉(zhuǎn)發(fā)出去,目的MAC先匹配list表,假設(shè)list沒有找到,那么我讓他指定了一個mac,比方廣播MAC。源MAC地址則賦值網(wǎng)卡的MAC地址。
注意,傳統(tǒng)以太網(wǎng)中數(shù)據(jù)長度為45-1500,那么我在構(gòu)建前把解析出的data作了下推斷長度再構(gòu)建,由于我已經(jīng)把sendbuffer聲明為一個固定長度了,為了防止越界,我先進行一個長度推斷。
//下面開始構(gòu)建幀發(fā)送//首先推斷data最大值小于1500if(strlen(data)<1500){//目的MACBYTE send_destmac[6];bool findMac = false;for(int c = 0;c<con;c++){if(ip->DestinationAddr.byte1 == list[c].ip.byte1&&ip->DestinationAddr.byte2 == list[c].ip.byte2&&ip->DestinationAddr.byte3 == list[c].ip.byte3&&ip->DestinationAddr.byte4 == list[c].ip.byte4){printf("Find its MAC!\n");findMac = true;send_destmac[0] = list[c].mac[0]; send_destmac[1] = list[c].mac[1];send_destmac[2] = list[c].mac[2];send_destmac[3] = list[c].mac[3];send_destmac[4] = list[c].mac[4];send_destmac[5] = list[c].mac[5];}}if(!findMac){send_destmac[0] = 0xff; send_destmac[1] = 0xff; send_destmac[2] = 0xff; send_destmac[3] = 0xff; send_destmac[4] = 0xff; send_destmac[5] = 0xff; }printf("destmac:%02x-%02x-%02x-%02x-%02x-%02x\n",send_destmac[0],send_destmac[1],send_destmac[2],send_destmac[3],send_destmac[4],send_destmac[5]);memcpy(send_ethernet.DestMAC, send_destmac, 6);//源MAC地址BYTE send_hostmac[6];//源MAC地址send_hostmac[0] = local_mac[0]; //賦值本地MAC地址send_hostmac[1] = local_mac[1];send_hostmac[2] = local_mac[2];send_hostmac[3] = local_mac[3];send_hostmac[4] = local_mac[4];send_hostmac[5] = local_mac[5];//賦值源MAC地址memcpy(send_ethernet.SourMAC, send_hostmac, 6);send_ethernet.EthType = htons(0x0800);//賦值SendBuffermemcpy(&SendBuffer, &send_ethernet, sizeof(struct EthernetHeader));以上僅僅是賦值了幀頭,至于IP頭,TCP頭,數(shù)據(jù)的賦值就參照0基礎(chǔ)功能的來賦值吧,不要忘了校驗和的檢驗。好,大體上就是這樣,接受來數(shù)據(jù)包并轉(zhuǎn)發(fā)出去的原理就是這樣。
三、接收
不用多改,就是0基礎(chǔ)功能中的接收,在此寫一寫小小的優(yōu)化措施,防止接收到過多的數(shù)據(jù)幀而造成不斷亂蹦,導(dǎo)致你看不到接收的東西。
在打印的時候加一個過濾就好了。部分代碼例如以下:
在main方法中提示用戶輸入要接收的IP地址
printf("請輸入要接收的IP地址,輸入0.0.0.0代表所有接收,請輸入\n");bool receiveAll = false;u_int ip1,ip2,ip3,ip4;bool legal = false;while(!legal){scanf_s("%d.%d.%d.%d",&ip1,&ip2,&ip3,&ip4);if(ip1==0&&ip2==0&&ip3==0&&ip4==0){receiveAll = true;legal = true;break;}if(ip1<0||ip1>255||ip2<0||ip2>255||ip3<0||ip3>255||ip4<1||ip4>254){legal = false;printf("對不起,IP輸入不合法,請又一次輸入:\n");}else{legal = true;}}打印時的推斷
if(receiveAll||(ip->SourceAddr.byte1==ip1&&ip->SourceAddr.byte2==ip2&&ip->SourceAddr.byte3==ip3&&ip->SourceAddr.byte4==ip4)){printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",ip->SourceAddr.byte1,ip->SourceAddr.byte2,ip->SourceAddr.byte3,ip->SourceAddr.byte4,sport,ip->DestinationAddr.byte1,ip->DestinationAddr.byte2,ip->DestinationAddr.byte3,ip->DestinationAddr.byte4,dport);printf("sou_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", sou_mac[0], sou_mac[1], sou_mac[2],sou_mac[3], sou_mac[4], sou_mac[5]);printf("des_mac:%02x-%02x-%02x-%02x-%02x-%02x\n", des_mac[0], des_mac[1], des_mac[2],des_mac[3], des_mac[4], des_mac[5]);printf("%s\n",data);printf("-----------------------------------------------------\n");}
?好,代碼就先放送這么多,詳細的實現(xiàn)僅僅要有了思路我相信肯定不難,如有問題,歡迎與我交流。
我的郵箱 1016903103@qq.com?
轉(zhuǎn)載于:https://www.cnblogs.com/mfrbuaa/p/4060226.html
總結(jié)
以上是生活随笔為你收集整理的Winpcap网络编程十之Winpcap实战,两台主机通过中间主机通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: const常量和readonly常量区别
- 下一篇: Eclipse快捷键 10个最有用的快捷