• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Xiao.Zhu C++

            Xiao.Zhu C++

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              29 隨筆 :: 14 文章 :: 17 評論 :: 0 Trackbacks
            1.概要
              相信在IPv6的時(shí)代到來之前,NAT仍然是解決大多數(shù)人上網(wǎng)的主要途徑,而且它在企業(yè)內(nèi)網(wǎng)Intranet中也扮演著十分重要的角色.
              NAT的全稱是Network Address Translator(網(wǎng)絡(luò)地址轉(zhuǎn)換),其主要作用是把內(nèi)網(wǎng)IP地址轉(zhuǎn)換成為全球唯一的可定位的外部IP地址,從而使得局域網(wǎng)內(nèi)的所有用戶可以通過一個(gè)或 者少數(shù)幾個(gè)IP地址與全球的Internet通信,不僅節(jié)約了IP地址,而且在一定程度上保護(hù)了內(nèi)部網(wǎng)絡(luò).
              由于工作需要,筆者希望編寫一個(gè)具有NAT功能的軟件,將同一個(gè)網(wǎng)段內(nèi)把本機(jī)設(shè)為網(wǎng)關(guān)的擁有私有IP的主機(jī)發(fā)來的數(shù)據(jù)包轉(zhuǎn)發(fā)到外部網(wǎng)絡(luò),并把響應(yīng)信息返回給對應(yīng)的主機(jī).這個(gè)問題在不同的層次上做就有不同的解決方案,由于筆者也是網(wǎng)絡(luò)新手,走了不少彎路:
              首先,企圖在用戶層利用原始套接字(Raw Socket)來實(shí)現(xiàn),但是系統(tǒng)擁有對未開放端口的自動(dòng)復(fù)位功能,每當(dāng)我們轉(zhuǎn)發(fā)一個(gè)數(shù)據(jù)包時(shí),需要占用系統(tǒng)的一個(gè)端口,但是這點(diǎn)系統(tǒng)并不知道,它接收到對 于這個(gè)端口的回應(yīng)信息時(shí),會認(rèn)為本端口不存在,并發(fā)送一個(gè)帶有復(fù)位標(biāo)志的數(shù)據(jù)包請求對方斷開連接.這就阻攔了所有非本機(jī)請求的連接,所以這個(gè)方案首先被否 定了.
              隨后,不得不往系統(tǒng)下面走,準(zhǔn)備在核心態(tài)實(shí)現(xiàn).當(dāng)然越簡單越好,于是筆者選擇了Filter-Hook驅(qū)動(dòng).Filter-Hook Driver, 事實(shí)上不是一種新的網(wǎng)絡(luò)驅(qū)動(dòng),它只是擴(kuò)展了IP過濾驅(qū)動(dòng)(IP Filter Driver)的功能,是一種內(nèi)核模式驅(qū)動(dòng)(Kernel Mode Driver). 在Filter-Hook Driver中我們提供回調(diào)函數(shù)(callback),然后使用IP Filter Driver注冊回調(diào)函數(shù)。這樣當(dāng)數(shù)據(jù)包發(fā)送和接收時(shí),IP Filter Driver會調(diào)用回調(diào)函數(shù)。可惜夢想再一次破滅,這個(gè)回調(diào)函數(shù)的返回值只有PF_FORWARD,PF_DROP,PF_PASS三種,并不能把修改后 的數(shù)據(jù)包主動(dòng)發(fā)送出去.
              只有在向底層走了,NDIS應(yīng)該是必經(jīng)之路.而且經(jīng)過兩次失敗,發(fā)現(xiàn)閉門造車是不可行的,偶然在網(wǎng)上搜索到了幾篇文章,聽說在NDIS的中間層驅(qū)動(dòng)中可以實(shí)現(xiàn)NAT,新的探索之路就這樣開始了......

            2.NAT簡介
              NAT(Network Address Translator)的出現(xiàn)并不是偶然的,一方面是由于IPv4的創(chuàng)造者們沒有想到,Internet以及TCP/IP發(fā)展如此迅速,在他們還們完全享 受TCP/IP的成功帶來的快感之前,32位的IP地址竟然不夠用了;另一方面我們必須保證某些特殊的主機(jī)在于局域網(wǎng)絡(luò)連接的同時(shí),保持對外界直接曝光, 但是由需要與外界在受控的情況下通訊.下圖是一個(gè)典型的NAT示意.
                \ | /             .                     /
              +---------------+ WAN   .       +-----------------+/
              |Regional Router|----------------------|Stub Router w/NAT|---
              +---------------+       .       +-----------------+\
                                .               |     \
                                .               | LAN
                                .           ---------------
                            Stub border

              下面舉一個(gè)具體的例子說明兩個(gè)處于內(nèi)網(wǎng)的主機(jī)是如何通過NAT通信的
                                      \ | /
                                    +---------------+
                                    |Regional Router|
                                    +---------------+
                                  WAN |       | WAN
                                      |       |
                        Stub A .............|....   ....|............ Stub B
                                      |       |
                          {s=198.76.29.7,^ |       | v{s=198.76.29.7,
                          d=198.76.28.4}^ |       | v d=198.76.28.4}
                          +-----------------+     +-----------------+
                          |Stub Router w/NAT|     |Stub Router w/NAT|
                          +-----------------+     +-----------------+
                              |                 |
                              | LAN           LAN |
                          -------------         -------------
                                  |           |
                      {s=10.33.96.5, ^ |           | v{s=198.76.29.7,
                      d=198.76.28.4}^ +--+         +--+ v d=10.81.13.22}
                                |--|         |--|
                                /____\       /____\
                              10.33.96.5     10.81.13.22

            圖 中有兩個(gè)殘樁網(wǎng)絡(luò)A和B,現(xiàn)在假設(shè)A中的一臺主機(jī)10.33.96.5需要同B中的10.81.13.22通信,它必須把自己發(fā)送的數(shù)據(jù)報(bào)的目的地址設(shè)置 為B的一個(gè)外網(wǎng)地址198.76.28.4,并在NAT中把源地址轉(zhuǎn)換成A的外部地址198.76.29.7,才能使數(shù)據(jù)包順利抵達(dá)廣域網(wǎng)中的路由器,并 轉(zhuǎn)到B,在由B網(wǎng)的NAT把數(shù)據(jù)包發(fā)送給10.81.13.22.
              隨著NAT多年的發(fā)展,出現(xiàn)了很多不同風(fēng)格,應(yīng)用于不同場合的NAT.筆者實(shí)現(xiàn)的是傳統(tǒng)NAT中的一種特殊情況NAPT(Network Address Port Translation),它把所有內(nèi)網(wǎng)的IP地址都轉(zhuǎn)換成同一個(gè)外部IP地址,并通過不同的端口來區(qū)分各個(gè)不同的內(nèi)部主機(jī).

            3.中間層驅(qū)動(dòng)NDIS Intermediate Drivers
              所謂中間層驅(qū)動(dòng)是指位于微端口和協(xié)議之間的驅(qū)動(dòng),實(shí)際上它是微軟在網(wǎng)絡(luò)驅(qū)動(dòng)中留出來的接口,便于用戶實(shí)現(xiàn)自己對數(shù)據(jù)包的處理.在協(xié)議驅(qū)動(dòng)層看來,它就是微 端口;在微端口看來,它又是協(xié)議層驅(qū)動(dòng).因此,如果需要在此層實(shí)現(xiàn)自己對數(shù)據(jù)包的處理函數(shù),不僅要在上邊緣注冊MiniportXxx Function,還要在下邊緣注冊ProtocolXxx Function.一般在這個(gè)層次做工作的同志都會學(xué)習(xí)并了解DDK的一個(gè)經(jīng)典Sample:Passthru.如果對它不了解,可以去看看 Addylee前幾天的文章"基于PassThru的NDIS中間層驅(qū)動(dòng)程序擴(kuò)展",講得很清晰.

            4.NAPT的具體實(shí)現(xiàn)
              程序整體框架依然是基于PaaThru的,具體要注意的問題有以下幾點(diǎn):
            4.1 轉(zhuǎn)發(fā)表的維護(hù)
            typedef struct _PortNode
            {
              USHORT inport;               //內(nèi)網(wǎng)端口
              USHORT export;           //轉(zhuǎn)發(fā)端口
              USHORT report;           //遠(yuǎn)程端口
              ULONG inip;           //內(nèi)網(wǎng)IP
              ULONG reip;           //遠(yuǎn)程IP
              struct _PortNode * next;         //鏈表指針
            }PortNode;

            PortNode * first = NULL;             //全局變量,轉(zhuǎn)發(fā)表的頭結(jié)點(diǎn)

            NTSTATUS
            DriverEntry(
              IN   PDRIVER_OBJECT     DriverObject,
              IN   PUNICODE_STRING     RegistryPath
              )
            {
                ......
              Status = NdisAllocateMemory(&first,sizeof(PortNode), 0,HighestAcceptableMax);
              if(Status == NDIS_STATUS_SUCCESS)
              {
                NdisZeroMemory(first,sizeof(PortNode));
                //首結(jié)點(diǎn)的inip表示本主機(jī)地址
                first->inip = 本主機(jī)IP        
                //首結(jié)點(diǎn)的reip表示本主機(jī)所在的網(wǎng)絡(luò)地址
                first->reip = first->inip & 0x00ffffff;
              }
              ......
            }

            4.2 校驗(yàn)和的計(jì)算
            USHORT CheckSum(USHORT *buffer, int size)
            {
              unsigned long cksum=0;
              while(size >1)
              {
                cksum += * buffer++;
                size -=sizeof(USHORT);
              }
              if(size)
              {
                cksum += *(UCHAR*)buffer;
              }
              cksum = (cksum >> 16) + (cksum & 0xffff);
              cksum += (cksum >>16);
              return (USHORT)(~cksum);
            }
              IP TCP UDP三種包校驗(yàn)和的計(jì)算方法是一致的,本文采用的方法是簡單地重新計(jì)算整個(gè)包的校驗(yàn)和,在RFC1631中,作者提出了一種差量計(jì)算法以提高計(jì)算速度,并且給出了C語言版的源代碼.

            4.3 對收到的數(shù)據(jù)包的過濾和轉(zhuǎn)發(fā)
            INT
            PtReceivePacket(
              IN   NDIS_HANDLE         ProtocolBindingContext,
              IN   PNDIS_PACKET     Packet
              )
            {
              ......

             
              PUCHAR     pPacketContent;
                PUCHAR     pBuf;
                UINT       BufLength;
                MDL       * pNext;
                UINT       i,j;
              BOOLEAN     transflag = FALSE;
              PNDIS_BUFFER MyBuffer;
              PIP_Header   pIPHeader;
             
              ......

              NdisDprAllocatePacket(&Status,
                              &MyPacket,
                              pAdapt->RecvPacketPoolHandle);

              if(Status == NDIS_STATUS_SUCCESS)
              {
                //add by thinking 06.6.3
                //把數(shù)據(jù)包內(nèi)容從Packet拷貝到pPacketContent
                Status= NdisAllocateMemory( &pPacketContent, 2000, 0,HighestAcceptableMax);
                if (Status!=NDIS_STATUS_SUCCESS )   return Status;
                NdisZeroMemory (pPacketContent, 2000);
                NdisQueryBufferSafe(Packet->Private.Head, &pBuf, &BufLength, 32 );
                NdisMoveMemory(pPacketContent, pBuf, BufLength);
                i = BufLength;
                pNext = Packet->Private.Head;
                for(;;)
                {
                    if(pNext == Packet->Private.Tail)
                      break;
                    pNext = pNext->Next;
                    if(pNext == NULL)
                      break;
                    NdisQueryBufferSafe(pNext,&pBuf,&BufLength,32);
                    NdisMoveMemory(pPacketContent+i,pBuf,BufLength);
                    i+=BufLength;
                }
                if(pPacketContent[12] == 8 && pPacketContent[13] == 0 ) //is ip packet
                {
                    ULONG netip;
                    pIPHeader = (PIP_Header)(pPacketContent+14);
                    netip = pIPHeader->ipSource & 0x00ffffff;
                    //對收到的數(shù)據(jù)包進(jìn)行過濾,只轉(zhuǎn)發(fā)需要轉(zhuǎn)發(fā)的包
                    if(pIPHeader->ipDestination == first->inip && netip != first->reip)
                    //如果目的地址是本主機(jī),并且源IP不是本網(wǎng)段地址,則轉(zhuǎn)發(fā)給內(nèi)網(wǎng)主機(jī)
                    {
                      DbgPrint("\nTransInPacket...\n");
                      for(j=0;j<=i;j++)
                        DbgPrint("%x ",pPacketContent[j]);
                      //修改發(fā)給內(nèi)網(wǎng)的數(shù)據(jù)包頭
                      transflag = TransIn(pPacketContent);
                    }
                    else if(pIPHeader->ipDestination != 0xffffffff &&
                      (pIPHeader->ipDestination & 0x00ffffff) != first->reip &&
                      netip == first->reip)
                    //如果目的地址不是廣播地址,而且是外網(wǎng)地址,源地址是內(nèi)網(wǎng)IP,則轉(zhuǎn)發(fā)給外網(wǎng)
                    {
                      DbgPrint("\nTransOutPacket...\n");
                      for(j=0;j<=i;j++)
                        DbgPrint("%x ",pPacketContent[j]);
                      //修改發(fā)給外網(wǎng)的數(shù)據(jù)包頭
                      transflag = TransOut(pPacketContent);
                    }
                }

                if(!transflag)
                {
                      //按照原來的方式往上提交  
                      ......
                }
                else
                {
                    //轉(zhuǎn)發(fā)的一段關(guān)鍵代碼
                    NdisAllocateBuffer(&Status,&MyBuffer,pAdapt->SendPacketPoolHandle,pPacketContent,i);
                    NdisChainBufferAtFront(MyPacket, MyBuffer);
                    Resvd =(PRSVD)(MyPacket->ProtocolReserved);
                    Resvd->OriginalPkt = MyPacket;
                      MyPacket->Private.Head->Next = NULL;
                    MyPacket->Private.Tail = NULL;
                    NdisSetPacketFlags(MyPacket, NDIS_FLAGS_DONT_LOOPBACK);
                    NdisReturnPackets(&Packet, 1);
                    NdisSend(&Status,pAdapt->BindingHandle,MyPacket);
                    if(Status != NDIS_STATUS_PENDING)
                    {
                      NdisUnchainBufferAtFront(MyPacket ,&MyBuffer); //從MyPacket中解除buffer
                      NdisQueryBufferSafe(MyBuffer, &pPacketContent, &BufLength,32 );
                      if(pPacketContent != NULL)
                        NdisFreeMemory(pPacketContent,BufLength, 0);
                      NdisFreeBuffer(MyBuffer);
                    }
                    return 0;
                }
              ......
            }

            4.4 數(shù)據(jù)包頭的修改
              根據(jù)具體情況修改數(shù)據(jù)包的IP包頭,TCP包頭,或者UDP包頭,并且在修改的同時(shí)繼續(xù)維護(hù)轉(zhuǎn)發(fā)表,下面只給出TransIn的代碼,TransOut與其原理相同.
            BOOLEAN TransIn(PUCHAR pPacketContent)
            {
              PortNode * inmap;
              PIP_Header pIPHeader = (PIP_Header)(pPacketContent+14);
              USHORT iphdrlen = (pIPHeader->iphVerLen & 0x0f) * sizeof(ULONG);
              UCHAR checkbuff[2000] = {0};

              if(pIPHeader->ipProtocol == 6)
              {
                PTCP_Header pTCPHeader;
                USHORT tcphdrlen;
                pTCPHeader = (PTCP_Header)(pPacketContent+14 + iphdrlen);
                //tcphdrlen = ((pTCPHeader->dataoffset & 0xf0) >> 4) * sizeof(ULONG);
                tcphdrlen = htons(pIPHeader->ipLength) - iphdrlen;
                inmap = InMapping(pIPHeader->ipSource,pTCPHeader->sourcePort,
                    pTCPHeader->destinationPort);
                if(inmap == NULL)
                    return FALSE;

                //修改目的地址和目的端口,校驗(yàn)和
                pIPHeader->ipDestination = inmap->inip;
                pTCPHeader->destinationPort = inmap->inport;
                pIPHeader->ipChecksum = 0;
                pTCPHeader->checksum = 0;

                //填充TCP偽首部
                psdhdr.saddr = pIPHeader->ipSource;
                psdhdr.daddr = pIPHeader->ipDestination;
                psdhdr.len = htons(tcphdrlen);
                psdhdr.mbz = 0;
                psdhdr.proto = 6;

                //計(jì)算TCP首部校驗(yàn)和
                NdisMoveMemory(checkbuff,&psdhdr,sizeof(psdhdr));
                NdisMoveMemory(checkbuff+sizeof(psdhdr),pTCPHeader,tcphdrlen);
                pTCPHeader->checksum = CheckSum((USHORT *)checkbuff,sizeof(psdhdr)+tcphdrlen);

                //計(jì)算IP首部校驗(yàn)和
                pIPHeader->ipChecksum = CheckSum((USHORT *)pIPHeader,iphdrlen);

                return TRUE;
              }
              else if(pIPHeader->ipProtocol == 17)
              {
                PUDP_Header pUDPHeader;
                USHORT udplen;
                pUDPHeader = (PUDP_Header)(pPacketContent+14 + iphdrlen);
                udplen = htons(pUDPHeader->len);
                inmap = InMapping(pIPHeader->ipSource,pUDPHeader->sourcePort,
                    pUDPHeader->destinationPort);
                if(inmap == NULL)
                    return FALSE;

                //修改目的地址和目的端口,校驗(yàn)和
                pIPHeader->ipDestination = inmap->inip;
                pUDPHeader->destinationPort = inmap->inport;
                pIPHeader->ipChecksum = 0;
                pUDPHeader->checksum = 0;

                //填充UDP偽首部
                psdhdr.saddr = pIPHeader->ipSource;
                psdhdr.daddr = pIPHeader->ipDestination;
                psdhdr.len = pUDPHeader->len;
                psdhdr.mbz = 0;
                psdhdr.proto = 17;

                //計(jì)算UDP校驗(yàn)和,包括所有UDP數(shù)據(jù)
                NdisMoveMemory(checkbuff,&psdhdr,sizeof(psdhdr));
                NdisMoveMemory(checkbuff+sizeof(psdhdr),pUDPHeader,udplen);
                pUDPHeader->checksum = CheckSum((USHORT *)checkbuff,sizeof(psdhdr)+udplen);

                //計(jì)算IP首部校驗(yàn)和
                pIPHeader->ipChecksum = CheckSum((USHORT *)pIPHeader,iphdrlen);

                return TRUE;
              }
              else
                return FALSE;
            }

            5.小結(jié)
              本文簡單介紹了傳統(tǒng)NAT在中間層驅(qū)動(dòng)中的實(shí)現(xiàn),很多地方都可以進(jìn)行改進(jìn).例如:校驗(yàn)和的計(jì)算可以采用差量計(jì)算法以減少計(jì)算延遲;轉(zhuǎn)發(fā)表的維護(hù)可以采用樹 型結(jié)構(gòu)(而不是本文中的鏈表)以減少轉(zhuǎn)發(fā)表的查找時(shí)間;定時(shí)對轉(zhuǎn)發(fā)表進(jìn)行清理,釋放長時(shí)間不用的端口,以節(jié)約系統(tǒng)資源;構(gòu)建ARP機(jī)制,并動(dòng)態(tài)維護(hù)相關(guān)主 機(jī)的MAC地址;通過共享內(nèi)存或者修改驅(qū)動(dòng)對象的DispatchTable與用戶層進(jìn)行通信,從而動(dòng)態(tài)調(diào)整驅(qū)動(dòng)功能.
            posted on 2007-05-06 18:05 Xiao.Zhu 閱讀(326) 評論(0)  編輯 收藏 引用
            A级毛片无码久久精品免费| 日本一区精品久久久久影院| 久久婷婷人人澡人人| yy6080久久| 久久久av波多野一区二区| 国产精品永久久久久久久久久| 久久久久国产亚洲AV麻豆| 中文字幕热久久久久久久| 国产精品久久久久久吹潮| 欧美亚洲日本久久精品| 精品人妻伦一二三区久久| 亚洲中文字幕伊人久久无码| 99久久人妻无码精品系列| 无码国内精品久久综合88| 99久久免费国产精品| 久久人人爽人人爽人人片AV不 | 久久久久久精品免费免费自慰| 久久精品麻豆日日躁夜夜躁| 老司机午夜网站国内精品久久久久久久久 | 国产一级做a爰片久久毛片| 久久久久这里只有精品 | 久久无码人妻精品一区二区三区| 久久久久噜噜噜亚洲熟女综合| 久久久久久亚洲Av无码精品专口| 久久这里只有精品视频99| 伊人久久精品线影院| 久久久国产精品福利免费| 精品少妇人妻av无码久久| 久久午夜伦鲁片免费无码| 性色欲网站人妻丰满中文久久不卡| 精品久久久久久久久久久久久久久| 国产精品久久成人影院| 久久AV高清无码| AV无码久久久久不卡网站下载| 综合人妻久久一区二区精品| 亚洲午夜福利精品久久| 久久国产精品波多野结衣AV| 久久久久国产视频电影| 久久影院久久香蕉国产线看观看| 亚洲人成无码www久久久| 久久综合久久美利坚合众国|