| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 | /* *  TAP-Windows -- A kernel driver to provide virtual tap *                 device functionality on Windows. * *  This code was inspired by the CIPE-Win32 driver by Damion K. Wilson. * *  This source code is Copyright (C) 2002-2014 OpenVPN Technologies, Inc., *  and is released under the GPL version 2 (see below). * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License version 2 *  as published by the Free Software Foundation. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program (see the file COPYING included with this *  distribution); if not, write to the Free Software Foundation, Inc., *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA *///// Include files.//#include "tap.h"//======================================================================// TAP Receive Path Support//======================================================================#ifdef ALLOC_PRAGMA#pragma alloc_text( PAGE, TapDeviceWrite)#endif // ALLOC_PRAGMA//===============================================================// Used in cases where internally generated packets such as// ARP or DHCP replies must be returned to the kernel, to be// seen as an incoming packet "arriving" on the interface.//===============================================================VOIDIndicateReceivePacket(    __in PTAP_ADAPTER_CONTEXT  Adapter,    __in PUCHAR packetData,    __in const unsigned int packetLength    ){    PUCHAR  injectBuffer;    //    // Handle miniport Pause    // ---------------------    // NDIS 6 miniports implement a temporary "Pause" state normally followed    // by the Restart. While in the Pause state it is forbidden for the miniport    // to indicate receive NBLs.    //    // That is: The device interface may be "up", but the NDIS miniport send/receive    // interface may be temporarily "down".    //    // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas inject path    // the code below will simply ignore inject packets passed to the driver while    // the miniport is in the Paused state.    //    // The correct implementation is to go ahead and build the NBLs corresponding    // to the inject packet - but queue them. When Restart is entered the    // queued NBLs would be dequeued and indicated to the host.    //    if(tapAdapterSendAndReceiveReady(Adapter) != NDIS_STATUS_SUCCESS)    {        DEBUGP (("[%s] Lying send in IndicateReceivePacket while adapter paused\n",            MINIPORT_INSTANCE_ID (Adapter)));        return;    }    // Allocate flat buffer for packet data.    injectBuffer = (PUCHAR )NdisAllocateMemoryWithTagPriority(                        Adapter->MiniportAdapterHandle,                        packetLength,                        TAP_RX_INJECT_BUFFER_TAG,                        NormalPoolPriority                        );    if( injectBuffer)    {        PMDL    mdl;        // Copy packet data to flat buffer.        NdisMoveMemory (injectBuffer, packetData, packetLength);        // Allocate MDL for flat buffer.        mdl = NdisAllocateMdl(                Adapter->MiniportAdapterHandle,                injectBuffer,                packetLength                );        if( mdl )        {            PNET_BUFFER_LIST    netBufferList;            mdl->Next = NULL;   // No next MDL            // Allocate the NBL and NB. Link MDL chain to NB.            netBufferList = NdisAllocateNetBufferAndNetBufferList(                                Adapter->ReceiveNblPool,                                0,                  // ContextSize                                0,                  // ContextBackFill                                mdl,                // MDL chain                                0,                                packetLength                                );            if(netBufferList != NULL)            {                ULONG       receiveFlags = 0;                LONG        nblCount;                NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL                if(KeGetCurrentIrql() == DISPATCH_LEVEL)                {                    receiveFlags |= NDIS_RECEIVE_FLAGS_DISPATCH_LEVEL;                }                // Set flag indicating that this is an injected packet                TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);                TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED);                netBufferList->MiniportReserved[0] = NULL;                netBufferList->MiniportReserved[1] = NULL;                // Increment in-flight receive NBL count.                nblCount = NdisInterlockedIncrement(&Adapter->ReceiveNblInFlightCount);                ASSERT(nblCount > 0 );                netBufferList->SourceHandle = Adapter->MiniportAdapterHandle;                //                // Indicate the packet                // -------------------                // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length                // contains the complete packet including Ethernet header and payload.                //                NdisMIndicateReceiveNetBufferLists(                    Adapter->MiniportAdapterHandle,                    netBufferList,                    NDIS_DEFAULT_PORT_NUMBER,                    1,      // NumberOfNetBufferLists                    receiveFlags                    );                return;            }            else            {                DEBUGP (("[%s] NdisAllocateNetBufferAndNetBufferList failed in IndicateReceivePacket\n",                    MINIPORT_INSTANCE_ID (Adapter)));                NOTE_ERROR ();                NdisFreeMdl(mdl);                NdisFreeMemory(injectBuffer,0,0);            }        }        else        {            DEBUGP (("[%s] NdisAllocateMdl failed in IndicateReceivePacket\n",                MINIPORT_INSTANCE_ID (Adapter)));            NOTE_ERROR ();            NdisFreeMemory(injectBuffer,0,0);        }    }    else    {        DEBUGP (("[%s] NdisAllocateMemoryWithTagPriority failed in IndicateReceivePacket\n",            MINIPORT_INSTANCE_ID (Adapter)));        NOTE_ERROR ();    }}VOIDtapCompleteIrpAndFreeReceiveNetBufferList(    __in  PTAP_ADAPTER_CONTEXT  Adapter,    __in  PNET_BUFFER_LIST      NetBufferList,  // Only one NB here...    __in  NTSTATUS              IoCompletionStatus    ){    PIRP    irp;    ULONG   frameType, netBufferCount, byteCount;    LONG    nblCount;    // Fetch NB frame type.    frameType = tapGetNetBufferFrameType(NET_BUFFER_LIST_FIRST_NB(NetBufferList));    // Fetch statistics for all NBs linked to the NB.    netBufferCount = tapGetNetBufferCountsFromNetBufferList(                        NetBufferList,                        &byteCount                        );    // Update statistics by frame type    if(IoCompletionStatus == STATUS_SUCCESS)    {        switch(frameType)        {        case NDIS_PACKET_TYPE_DIRECTED:            Adapter->FramesRxDirected += netBufferCount;            Adapter->BytesRxDirected += byteCount;            break;        case NDIS_PACKET_TYPE_BROADCAST:            Adapter->FramesRxBroadcast += netBufferCount;            Adapter->BytesRxBroadcast += byteCount;            break;        case NDIS_PACKET_TYPE_MULTICAST:            Adapter->FramesRxMulticast += netBufferCount;            Adapter->BytesRxMulticast += byteCount;            break;        default:            ASSERT(FALSE);            break;        }    }    //    // Handle P2P Packet    // -----------------    // Free MDL allocated for P2P Ethernet header.    //    if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_P2P))    {        PNET_BUFFER     netBuffer;        PMDL            mdl;        netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);        mdl = NET_BUFFER_FIRST_MDL(netBuffer);        mdl->Next = NULL;        NdisFreeMdl(mdl);    }    //    // Handle Injected Packet    // -----------------------    // Free MDL and data buffer allocated for injected packet.    //    if(TAP_RX_NBL_FLAG_TEST(NetBufferList,TAP_RX_NBL_FLAGS_IS_INJECTED))    {        PNET_BUFFER     netBuffer;        PMDL            mdl;        PUCHAR          injectBuffer;        netBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList);        mdl = NET_BUFFER_FIRST_MDL(netBuffer);        injectBuffer = (PUCHAR )MmGetSystemAddressForMdlSafe(mdl,NormalPagePriority);        if(injectBuffer)        {            NdisFreeMemory(injectBuffer,0,0);        }        NdisFreeMdl(mdl);    }    //    // Complete the IRP    //    irp = (PIRP )NetBufferList->MiniportReserved[0];    if(irp)    {        irp->IoStatus.Status = IoCompletionStatus;        IoCompleteRequest(irp, IO_NO_INCREMENT);    }    // Decrement in-flight receive NBL count.    nblCount = NdisInterlockedDecrement(&Adapter->ReceiveNblInFlightCount);    ASSERT(nblCount >= 0 );    if (0 == nblCount)    {        NdisSetEvent(&Adapter->ReceiveNblInFlightCountZeroEvent);    }    // Free the NBL    NdisFreeNetBufferList(NetBufferList);}VOIDAdapterReturnNetBufferLists(    __in  NDIS_HANDLE             MiniportAdapterContext,    __in  PNET_BUFFER_LIST        NetBufferLists,    __in  ULONG                   ReturnFlags    ){    PTAP_ADAPTER_CONTEXT    adapter = (PTAP_ADAPTER_CONTEXT )MiniportAdapterContext;    PNET_BUFFER_LIST        currentNbl, nextNbl;    UNREFERENCED_PARAMETER(ReturnFlags);    //    // Process each NBL individually    //    currentNbl = NetBufferLists;    while (currentNbl)    {        PNET_BUFFER_LIST    nextNbl;        nextNbl = NET_BUFFER_LIST_NEXT_NBL(currentNbl);        NET_BUFFER_LIST_NEXT_NBL(currentNbl) = NULL;        // Complete write IRP and free NBL and associated resources.        tapCompleteIrpAndFreeReceiveNetBufferList(            adapter,            currentNbl,            STATUS_SUCCESS            );        // Move to next NBL        currentNbl = nextNbl;    }}// IRP_MJ_WRITE callback.NTSTATUSTapDeviceWrite(    PDEVICE_OBJECT DeviceObject,    PIRP Irp    ){    NTSTATUS                ntStatus = STATUS_SUCCESS;// Assume success    PIO_STACK_LOCATION      irpSp;// Pointer to current stack location    PTAP_ADAPTER_CONTEXT    adapter = NULL;    ULONG                   dataLength;    PAGED_CODE();    irpSp = IoGetCurrentIrpStackLocation( Irp );    //    // Fetch adapter context for this device.    // --------------------------------------    // Adapter pointer was stashed in FsContext when handle was opened.    //    adapter = (PTAP_ADAPTER_CONTEXT )(irpSp->FileObject)->FsContext;    ASSERT(adapter);    //    // Sanity checks on state variables    //    if (!tapAdapterReadAndWriteReady(adapter))    {        //DEBUGP (("[%s] Interface is down in IRP_MJ_WRITE\n",        //    MINIPORT_INSTANCE_ID (adapter)));        //NOTE_ERROR();        Irp->IoStatus.Status = ntStatus = STATUS_CANCELLED;        Irp->IoStatus.Information = 0;        IoCompleteRequest (Irp, IO_NO_INCREMENT);        return ntStatus;    }    // Save IRP-accessible copy of buffer length    Irp->IoStatus.Information = irpSp->Parameters.Write.Length;    if (Irp->MdlAddress == NULL)    {        DEBUGP (("[%s] MdlAddress is NULL for IRP_MJ_WRITE\n",            MINIPORT_INSTANCE_ID (adapter)));        NOTE_ERROR();        Irp->IoStatus.Status = ntStatus = STATUS_INVALID_PARAMETER;        Irp->IoStatus.Information = 0;        IoCompleteRequest (Irp, IO_NO_INCREMENT);        return ntStatus;    }    //    // Try to get a virtual address for the MDL.    //    NdisQueryMdl(        Irp->MdlAddress,        &Irp->AssociatedIrp.SystemBuffer,        &dataLength,        NormalPagePriority        );    if (Irp->AssociatedIrp.SystemBuffer == NULL)    {        DEBUGP (("[%s] Could not map address in IRP_MJ_WRITE\n",            MINIPORT_INSTANCE_ID (adapter)));        NOTE_ERROR();        Irp->IoStatus.Status = ntStatus = STATUS_INSUFFICIENT_RESOURCES;        Irp->IoStatus.Information = 0;        IoCompleteRequest (Irp, IO_NO_INCREMENT);        return ntStatus;    }    ASSERT(dataLength == irpSp->Parameters.Write.Length);    Irp->IoStatus.Information = irpSp->Parameters.Write.Length;    //    // Handle miniport Pause    // ---------------------    // NDIS 6 miniports implement a temporary "Pause" state normally followed    // by the Restart. While in the Pause state it is forbidden for the miniport    // to indicate receive NBLs.    //    // That is: The device interface may be "up", but the NDIS miniport send/receive    // interface may be temporarily "down".    //    // BUGBUG!!! In the initial implementation of the NDIS 6 TapOas receive path    // the code below will perform a "lying send" for write IRPs passed to the    // driver while the miniport is in the Paused state.    //    // The correct implementation is to go ahead and build the NBLs corresponding    // to the user-mode write - but queue them. When Restart is entered the    // queued NBLs would be dequeued and indicated to the host.    //    if(tapAdapterSendAndReceiveReady(adapter) == NDIS_STATUS_SUCCESS)    {        if (/*!adapter->m_tun &&*/ ((irpSp->Parameters.Write.Length) >= ETHERNET_HEADER_SIZE))        {            PNET_BUFFER_LIST    netBufferList;            DUMP_PACKET ("IRP_MJ_WRITE ETH",                (unsigned char *) Irp->AssociatedIrp.SystemBuffer,                irpSp->Parameters.Write.Length);            //=====================================================            // If IPv4 packet, check whether or not packet            // was truncated.            //=====================================================#if PACKET_TRUNCATION_CHECK            IPv4PacketSizeVerify (                (unsigned char *) Irp->AssociatedIrp.SystemBuffer,                irpSp->Parameters.Write.Length,                FALSE,                "RX",                &adapter->m_RxTrunc                );#endif            (Irp->MdlAddress)->Next = NULL; // No next MDL            // Allocate the NBL and NB. Link MDL chain to NB.            netBufferList = NdisAllocateNetBufferAndNetBufferList(                adapter->ReceiveNblPool,                0,                  // ContextSize                0,                  // ContextBackFill                Irp->MdlAddress,    // MDL chain                0,                dataLength                );            if(netBufferList != NULL)            {                LONG    nblCount;                NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL                // Stash IRP pointer in NBL MiniportReserved[0] field.                netBufferList->MiniportReserved[0] = Irp;                netBufferList->MiniportReserved[1] = NULL;                // This IRP is pended.                IoMarkIrpPending(Irp);                // This IRP cannot be cancelled while in-flight.                IoSetCancelRoutine(Irp,NULL);                TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);                // Increment in-flight receive NBL count.                nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount);                ASSERT(nblCount > 0 );                //                // Indicate the packet                // -------------------                // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length                // contains the complete packet including Ethernet header and payload.                //                NdisMIndicateReceiveNetBufferLists(                    adapter->MiniportAdapterHandle,                    netBufferList,                    NDIS_DEFAULT_PORT_NUMBER,                    1,      // NumberOfNetBufferLists                    0       // ReceiveFlags                    );                ntStatus = STATUS_PENDING;            }            else            {                DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n",                    MINIPORT_INSTANCE_ID (adapter)));                NOTE_ERROR ();                // Fail the IRP                Irp->IoStatus.Information = 0;                ntStatus = STATUS_INSUFFICIENT_RESOURCES;            }        }		/*        else if (adapter->m_tun && ((irpSp->Parameters.Write.Length) >= IP_HEADER_SIZE))        {            PETH_HEADER         p_UserToTap = &adapter->m_UserToTap;            PMDL                mdl;    // Head of MDL chain.            // For IPv6, need to use Ethernet header with IPv6 proto            if ( IPH_GET_VER( ((IPHDR*) Irp->AssociatedIrp.SystemBuffer)->version_len) == 6 )            {                p_UserToTap = &adapter->m_UserToTap_IPv6;            }            DUMP_PACKET2 ("IRP_MJ_WRITE P2P",                p_UserToTap,                (unsigned char *) Irp->AssociatedIrp.SystemBuffer,                irpSp->Parameters.Write.Length);            //=====================================================            // If IPv4 packet, check whether or not packet            // was truncated.            //=====================================================#if PACKET_TRUNCATION_CHECK            IPv4PacketSizeVerify (                (unsigned char *) Irp->AssociatedIrp.SystemBuffer,                irpSp->Parameters.Write.Length,                TRUE,                "RX",                &adapter->m_RxTrunc                );#endif            //            // Allocate MDL for Ethernet header            // --------------------------------            // Irp->AssociatedIrp.SystemBuffer with length irpSp->Parameters.Write.Length            // contains the only the Ethernet payload. Prepend the user-mode provided            // payload with the Ethernet header pointed to by p_UserToTap.            //            mdl = NdisAllocateMdl(                adapter->MiniportAdapterHandle,                p_UserToTap,                sizeof(ETH_HEADER)                );            if(mdl != NULL)            {                PNET_BUFFER_LIST    netBufferList;                // Chain user's Ethernet payload behind Ethernet header.                mdl->Next = Irp->MdlAddress;                (Irp->MdlAddress)->Next = NULL; // No next MDL                // Allocate the NBL and NB. Link MDL chain to NB.                netBufferList = NdisAllocateNetBufferAndNetBufferList(                    adapter->ReceiveNblPool,                    0,          // ContextSize                    0,          // ContextBackFill                    mdl,        // MDL chain                    0,                    sizeof(ETH_HEADER) + dataLength                    );                if(netBufferList != NULL)                {                    LONG        nblCount;                    NET_BUFFER_LIST_NEXT_NBL(netBufferList) = NULL; // Only one NBL                    // This IRP is pended.                    IoMarkIrpPending(Irp);                    // This IRP cannot be cancelled while in-flight.                    IoSetCancelRoutine(Irp,NULL);                    // Stash IRP pointer in NBL MiniportReserved[0] field.                    netBufferList->MiniportReserved[0] = Irp;                    netBufferList->MiniportReserved[1] = NULL;                    // Set flag indicating that this is P2P packet                    TAP_RX_NBL_FLAGS_CLEAR_ALL(netBufferList);                    TAP_RX_NBL_FLAG_SET(netBufferList,TAP_RX_NBL_FLAGS_IS_P2P);                    // Increment in-flight receive NBL count.                    nblCount = NdisInterlockedIncrement(&adapter->ReceiveNblInFlightCount);                    ASSERT(nblCount > 0 );                    //                    // Indicate the packet                    //                    NdisMIndicateReceiveNetBufferLists(                        adapter->MiniportAdapterHandle,                        netBufferList,                        NDIS_DEFAULT_PORT_NUMBER,                        1,      // NumberOfNetBufferLists                        0       // ReceiveFlags                        );                    ntStatus = STATUS_PENDING;                }                else                {                    mdl->Next = NULL;                    NdisFreeMdl(mdl);                    DEBUGP (("[%s] NdisMIndicateReceiveNetBufferLists failed in IRP_MJ_WRITE\n",                        MINIPORT_INSTANCE_ID (adapter)));                    NOTE_ERROR ();                    // Fail the IRP                    Irp->IoStatus.Information = 0;                    ntStatus = STATUS_INSUFFICIENT_RESOURCES;                }            }            else            {                DEBUGP (("[%s] NdisAllocateMdl failed in IRP_MJ_WRITE\n",                    MINIPORT_INSTANCE_ID (adapter)));                NOTE_ERROR ();                // Fail the IRP                Irp->IoStatus.Information = 0;                ntStatus = STATUS_INSUFFICIENT_RESOURCES;            }        }		*/        else        {            DEBUGP (("[%s] Bad buffer size in IRP_MJ_WRITE, len=%d\n",                MINIPORT_INSTANCE_ID (adapter),                irpSp->Parameters.Write.Length));            NOTE_ERROR ();            Irp->IoStatus.Information = 0;	// ETHERNET_HEADER_SIZE;            Irp->IoStatus.Status = ntStatus = STATUS_BUFFER_TOO_SMALL;        }    }    else    {        DEBUGP (("[%s] Lying send in IRP_MJ_WRITE while adapter paused\n",            MINIPORT_INSTANCE_ID (adapter)));        ntStatus = STATUS_SUCCESS;    }    if (ntStatus != STATUS_PENDING)    {        Irp->IoStatus.Status = ntStatus;        IoCompleteRequest(Irp, IO_NO_INCREMENT);    }    return ntStatus;}
 |