Winpcapネットワークプログラミング10のWinpcap実戦、2台のホストは中間ホストを通じて通信する


注:ソースコードなどの私は完全に公开することはできません.この文章はみんなのネットプログラミングや课程の设计に一定の考え方を提供します.
はい、今回私たちが完成しなければならない任務は:
2台のホストが中間ホストを介してデータ通信を完了する(ネットワーク層)
  • IPアドレスに基づく転送機能
  • を追加する.
  • ネットワーク層パッケージ
  • を追加する.
    実は最も主要なのはIPアドレスの転送機能に基づいて、ネット層のパッケージは実は私たちは初級機能の中ですでにできました.
    まず,実験の構想は,Aが中間ホストBを介してCにデータを送信することである.では、Bはルータとして、Bは2つのネットワークカードを傍受し、1つのネットワークカードから送られたデータは別のネットワークカードを通じて送られます.
    概略図は次のとおりです.
    A--------->B1===B2------------>C
    図から分かるように、Bホストの2つのNICデータは互いに通じ合い、AとB 1は1つのローカルエリアネットワーク内にあり、B 2とCは別のローカルエリアネットワーク内にある.
    例えば、今ルームメイトAは有線でインターネットを利用しています.私のパソコンBも有線でインターネットを利用しています.私たちの有線は同じローカルエリアネットワークにあります.私のパソコンBは同時に無線網が散らばっています.私の携帯電話Cはまたこの無線に接続されています.
    では、AからCへのデータ転送を実現するには、ルームメイトAが私の携帯電話Cにデータを送信することをシミュレートします.
    ルームメイトAは有線LANで私のNIC B 1にデータを送信し、B 1はNIC B 2を通じて無線LANにデータを転送し、無線LANを通じて私の携帯電話Cに到着した.
    Aの送信は1フレーム構築され、宛先MACアドレスはB 1、宛先IPはCである.Bは2つのネットワークカードを開き、B 1は受信データを傍受し、B 2ネットワークカードはARPプロトコルで所在する無線LAN内のIPとMACをスキャンし、BはAからのフレームを取得した後、そのIPアドレスとMACアドレスを解析し、さっきスキャンしたIPとMAC対応表に一致し、ソースMACをB 2ネットワークカードMACに変換し、目的MACをCのMACに変換し、IPは変わらず、データdataは変わらない.新しいフレームを構築して送信します.
    はい、考えは大体そうです.
    3つのプログラムが必要です.1つは送信、1つはルーティング、1つは受信です.だから全部で3つのプログラムが同時に実行されます.
    以上は私の大体の考え方ですが、間違いがあれば、指摘してください.コードで実装されました.
    コードはしばらく公開しないで、一部の重点コードの解析だけを提供します:
    一、送信側
    実は送信側と初級機能の送信差は多くありません
    個人が作成するインタラクティブなプロセスは次のとおりです.
    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    ,          IP  :
    192.168.1.3
              :
    im cqc
          :im cqc
    

    具体的なコードは解析されず、前の初級機能と同じです.
    二、道路由端
    まず、2つのNICをオンにして、2つのNICオブジェクトとプロセッサを宣言します.
    pcap_if_t  *d,*d2;					//        
    pcap_t *adhandle,*adhandle2;           //    , pcap_open     ,adhandle       ,adhandle2       

    1つは送信のために受信され、ここでadhandleは送信のために定義され、adhandle 2はデータを受信するために使用される.
    では、アダプタを開くとmainメソッドで、2つのNICを事前に開きます.
    int num;
    	printf("              :
    "); // scanf_s("%d",&num); // for(d=alldevs, i=0; i< num-1 ; d=d->next, i++); // , if((adhandle = pcap_open(d->name, // 65535, // PCAP_OPENFLAG_PROMISCUOUS, // 1000, // NULL, // errbuf // )) == NULL){ // , fprintf(stderr,"
    Unable to open the adapter. %s is not supported by WinPcap
    ", d->name); // pcap_freealldevs(alldevs); return -1; } int num2; printf(" :"); // scanf_s("%d",&num2); // // for(d2=alldevs, i=0; i< num2-1 ; d2=d2->next, i++); // if((adhandle2 = pcap_open(d2->name, // 65535, // PCAP_OPENFLAG_PROMISCUOUS, // 1000, // NULL, // errbuf // )) == NULL){ // , fprintf(stderr,"
    Unable to open the adapter. %s is not supported by WinPcap
    ", d2->name);

    次に、送信用handleプロセッサを用いてそのローカルエリアネットワークIPをスキャンし、ローカルエリアネットワーク内のMACアドレスを取得し、1つのテーブルに記録し、IPとMACの対応関係を格納する.
    このテーブルは構造体配列で保存できます.たとえば、次のようになります.
    struct ip_mac_list{
    	IpAddress ip;
    	unsigned char mac[6];
    };
    ip_mac_list  list[256];                       //  IP MAC      

    以上が準備作業であり、2つのNICのオープンを完了し、NICスキャンを送信してローカルエリアネットワークMACを取得し、次に最も重要なリスニングと転送を行います.
    では、これはどうしますか.では、新しいスレッドを開きます.
    新しいルーティングスレッドを宣言しましょう.
     
    DWORD WINAPI RouteThread(LPVOID lpParameter);

    では、スレッドはどんなパラメータを受信しますか?
    まず必要なのは2つのNICプロセッサで、mainメソッドで初期化されたadhandleとadhandle 2が作成されています.またalldevsもあります.このポインタを持ってデバイスリストを解放し、エラーが発生したときにリソースを解放して終了することができます.
    初級機能で宣言しました
    struct sparam sp; struct gparam gp;
    この2つがARPスレッドの送信とARPスレッドの受信の2つのパラメータであり,この機能を模倣して新しい構造体を定義する.
    struct rparam{
    	pcap_t *adhandle_rec;
    	pcap_t *adhandle_send;
    	pcap_if_t  * alldevs;       //       
    };

    mainメソッドで付与値を初期化します
     
    rp.adhandle_send = adhandle;
    	rp.adhandle_rec = adhandle2;
    	rp.alldevs = alldevs;

    パラメータとしてこのスレッドに転送
     
    routethread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) RouteThread, &rp,
    			0, NULL);

    4番目のパラメータはこの構造体を伝達することである.注意この文はmainメソッドに直接呼び出さないほうがよく、MACアドレスをすべて取得してからスレッドを開くことができます.
    では、次はこのスレッドが何をしているのか、コア部分だけを簡単に話します.
    まずこのスレッドを開いてからずっと実行されるので、参加できます.
    while((res = pcap_next_ex(adhandle2,&header,&pkt_data))>=0)

    このようなwhile判定文は、パケットの受信を常に傍受し、データを解析する.
     
    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        
    			ip = (IpHeader *) (pkt_data +14);    //14         
    			//  TCP     
    			ip_len = (ip->Version_HLen & 0xf) *4;
    			tcp = (TcpHeader *)((u_char *)ip+ip_len);
    			data = (char *)((u_char *)tcp+20);
    			printf("data:%s
    ",data); printf("ip:"); printf("%d.%d.%d.%d -> %d.%d.%d.%d
    ", 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
    ", 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
    ", des_mac[0], des_mac[1], des_mac[2], des_mac[3], des_mac[4], des_mac[5]);

    次に、データが受信されるたびに、新しいフレーム転送が行われ、宛先MACがリストテーブルに一致し、リストが見つからない場合は、ブロードキャストMACなどのmacを指定させます.ソースMACアドレスは、NICのMACアドレスを付与する.
    従来のイーサネットではデータ長が45-1500なので、構築前に解析したdataを長さを判断して構築しました.sendbufferを固定長と宣言したので、境界を越えるのを防ぐために、まず長さ判断を行います.
    //         
    		//    data     1500
    		if(strlen(data)<1500){
    			//  MAC
    			BYTE send_destmac[6];
    			bool findMac = false;
    			for(int c = 0;cDestinationAddr.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!
    "); 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
    ", 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); // SendBuffer memcpy(&SendBuffer, &send_ethernet, sizeof(struct EthernetHeader));

    以上はフレームヘッダを付与しただけで、IPヘッダ、TCPヘッダについては、データの付与は初級機能を参照して付与しましょう.チェックサムの検証を忘れないでください.はい、大体そうです.パケットを受け取って転送する原理はそうです.
    三、受信
    多く変更する必要はありません.初級機能の受信です.ここで小さな最適化措置を書いて、受信しすぎたデータフレームが絶えず跳ねて、受信したものが見えないことを防止します.
    印刷するときにフィルタを1つ加えればいいです.コードの一部は次のとおりです.
    mainメソッドで受信するIPアドレスの入力をユーザに促す
    printf("       IP  ,  0.0.0.0      ,   
    "); 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 , :
    "); }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
    ", 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
    ", 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
    ", des_mac[0], des_mac[1], des_mac[2], des_mac[3], des_mac[4], des_mac[5]); printf("%s
    ",data); printf("-----------------------------------------------------
    "); }

    はい、コードは先にこんなに多く放送して、具体的な実現は構想があれば私はきっと難しくないと信じて、もし問題があれば、私と交流することを歓迎します.
    メールアドレス[email protected]