【ChinaBeta.Cn 网盟学院】
4.3 对收到的数据包的过滤和转发 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 //把数据包内容从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; //对收到的数据包进行过滤,只转发需要转发的包 if(pIPHeader->ipDestination == first->inip && netip != first->reip) //如果目的地址是本主机,并且源IP不是本网段地址,则转发给内网主机 { DbgPrint("\nTransInPacket...\n"); for(j=0;j<=i;j++) DbgPrint("%x ",pPacketContent[j]); //修改发给内网的数据包头 transflag = TransIn(pPacketContent); } else if(pIPHeader->ipDestination != 0xffffffff && (pIPHeader->ipDestination & 0x00ffffff) != first->reip && netip == first->reip) //如果目的地址不是广播地址,而且是外网地址,源地址是内网IP,则转发给外网 { DbgPrint("\nTransOutPacket...\n"); for(j=0;j<=i;j++) DbgPrint("%x ",pPacketContent[j]); //修改发给外网的数据包头 transflag = TransOut(pPacketContent); } }
if(!transflag) { //按照原来的方式往上提交 ...... } else { //转发的一段关键代码 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 数据包头的修改 根据具体情况修改数据包的IP包头,TCP包头,或者UDP包头,并且在修改的同时继续维护转发表,下面只给出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;
//修改目的地址和目的端口,校验和 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;
//计算TCP首部校验和 NdisMoveMemory(checkbuff,&psdhdr,sizeof(psdhdr)); NdisMoveMemory(checkbuff+sizeof(psdhdr),pTCPHeader,tcphdrlen); pTCPHeader->checksum = CheckSum((USHORT *)checkbuff,sizeof(psdhdr)+tcphdrlen);
//计算IP首部校验和 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;
//修改目的地址和目的端口,校验和 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;
//计算UDP校验和,包括所有UDP数据 NdisMoveMemory(checkbuff,&psdhdr,sizeof(psdhdr)); NdisMoveMemory(checkbuff+sizeof(psdhdr),pUDPHeader,udplen); pUDPHeader->checksum = CheckSum((USHORT *)checkbuff,sizeof(psdhdr)+udplen);
//计算IP首部校验和 pIPHeader->ipChecksum = CheckSum((USHORT *)pIPHeader,iphdrlen);
return TRUE; } else return FALSE; }
5.小结 本文简单介绍了传统NAT在中间层驱动中的实现,很多地方都可以进行改进.例如:校验和的计算可以采用差量计算法以减少计算延迟;转发表的维护可以采用树型结构(而不是本文中的链表)以减少转发表的查找时间;定时对转发表进行清理,释放长时间不用的端口,以节约系统资源;构建ARP机制,并动态维护相关主机的MAC地址;通过共享内存或者修改驱动对象的DispatchTable与用户层进行通信,从而动态调整驱动功能.
参考文献: RFC1631: The IP Network Address Translator (NAT) RFC2663: IP Network Address Translator (NAT) Terminology and Considerations Addylee "基于PassThru的NDIS中间层驱动程序扩展" http://www.xfocus.net/articles/200605/865.html Jesús O "开发Windows 2000/XP下的防火墙" http://www.vckbase.com/document/viewdoc/?id=1067 上一页 [1] [2]
(责任编辑:hahack)
|
|