123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- /*
- * 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.
- //===============================================================
- VOID
- IndicateReceivePacket(
- __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 ();
- }
- }
- VOID
- tapCompleteIrpAndFreeReceiveNetBufferList(
- __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);
- }
- VOID
- AdapterReturnNetBufferLists(
- __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.
- NTSTATUS
- TapDeviceWrite(
- 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;
- }
|