X-Git-Url: http://git.jankratochvil.net/?p=reactos.git;a=blobdiff_plain;f=drivers%2Fnet%2Fnpf%2Fread.c;fp=drivers%2Fnet%2Fnpf%2Fread.c;h=75417440b0fa3959773236657486107954d8f200;hp=0000000000000000000000000000000000000000;hb=e3ed2d773259cc445c7ff8181ebd934931365328;hpb=d378c68f5a9bb25c9e671dacd482d2e25d211df3 diff --git a/drivers/net/npf/read.c b/drivers/net/npf/read.c new file mode 100644 index 0000000..7541744 --- /dev/null +++ b/drivers/net/npf/read.c @@ -0,0 +1,680 @@ +/* + * Copyright (c) 1999, 2000 + * Politecnico di Torino. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that: (1) source code distributions + * retain the above copyright notice and this paragraph in its entirety, (2) + * distributions including binary code include the above copyright notice and + * this paragraph in its entirety in the documentation or other materials + * provided with the distribution, and (3) all advertising materials mentioning + * features or use of this software display the following acknowledgement: + * ``This product includes software developed by the Politecnico + * di Torino, and its contributors.'' Neither the name of + * the University nor the names of its contributors may be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifdef _MSC_VER +#include +#include +#include +#include +#else +#include +#include + +#define NdisMoveMappedMemory(Destination,Source,Length) RtlCopyMemory(Destination,Source,Length) +#define NdisZeroMappedMemory(Destination,Length) RtlZeroMemory(Destination,Length) +#define NdisReinitializePacket(Packet) \ +{ \ + (Packet)->Private.Head = (PNDIS_BUFFER)NULL; \ + (Packet)->Private.ValidCounts = FALSE; \ +} + + +#endif + +#include "debug.h" +#include "packet.h" +#include "win_bpf.h" + +#include "tme.h" +#include "time_calls.h" + +extern struct time_conv G_Start_Time; // from openclos.c + +//------------------------------------------------------------------- + +UINT GetBuffOccupation(POPEN_INSTANCE Open) +{ + UINT Occupation; + + NdisAcquireSpinLock( &Open->BufLock ); + + if(Open->Btail >= Open->Bhead) Occupation = Open->Btail-Open->Bhead; + else Occupation = Open->BLastByte-Open->Bhead+Open->Btail; + + NdisReleaseSpinLock( &Open->BufLock ); + + return Occupation; +} + +//------------------------------------------------------------------- + +void PacketMoveMem(PVOID Destination, PVOID Source, ULONG Length, UINT *Bhead) +{ +ULONG WordLength; +UINT n,i,NBlocks; + + WordLength=Length>>2; + NBlocks=WordLength>>8; + + for(n=0;nFileObject->FsContext; + + if( Open->Bound == FALSE ){ + // The Network adapter was removed. + EXIT_FAILURE(0); + } + + if( Open->mode & MODE_DUMP && Open->DumpFileHandle == NULL ){ + // this instance is in dump mode, but the dump file has still not been opened + EXIT_FAILURE(0); + } + + //See if the buffer is full enough to be copied + if( GetBuffOccupation(Open) <= Open->MinToCopy || Open->mode & MODE_DUMP ) + { + //wait until some packets arrive or the timeout expires + if(Open->TimeOut.QuadPart != (LONGLONG)IMMEDIATE) + KeWaitForSingleObject(Open->ReadEvent, + UserRequest, + KernelMode, + TRUE, + (Open->TimeOut.QuadPart == (LONGLONG)0)? NULL: &(Open->TimeOut)); + + KeClearEvent(Open->ReadEvent); + + if(Open->mode & MODE_STAT){ //this capture instance is in statistics mode + CurrBuff=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress); + + //fill the bpf header for this packet + header=(struct bpf_hdr*)CurrBuff; + GET_TIME(&header->bh_tstamp,&G_Start_Time); + + if(Open->mode & MODE_DUMP){ + *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+16)=Open->DumpOffset.QuadPart; + header->bh_caplen=24; + header->bh_datalen=24; + Irp->IoStatus.Information = 24 + sizeof(struct bpf_hdr); + } + else{ + header->bh_caplen=16; + header->bh_datalen=16; + header->bh_hdrlen=sizeof(struct bpf_hdr); + Irp->IoStatus.Information = 16 + sizeof(struct bpf_hdr); + } + + *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr))=Open->Npackets.QuadPart; + *(LONGLONG*)(CurrBuff+sizeof(struct bpf_hdr)+8)=Open->Nbytes.QuadPart; + + //reset the countetrs + NdisAcquireSpinLock( &Open->CountersLock ); + Open->Npackets.QuadPart=0; + Open->Nbytes.QuadPart=0; + NdisReleaseSpinLock( &Open->CountersLock ); + + Irp->IoStatus.Status = STATUS_SUCCESS; + IoCompleteRequest(Irp, IO_NO_INCREMENT); + + return STATUS_SUCCESS; + } + + if(Open->mode==MODE_MON) //this capture instance is in monitor mode + { + PTME_DATA data; + ULONG cnt; + ULONG block_size; + PUCHAR tmp; + + UserPointer=MmGetSystemAddressForMdl(Irp->MdlAddress); + + if ((!IS_VALIDATED(Open->tme.validated_blocks,Open->tme.active_read))||(IrpSp->Parameters.Read.Lengthbh_tstamp,&G_Start_Time); + + + header->bh_hdrlen=sizeof(struct bpf_hdr); + + + //moves user memory pointer + UserPointer+=sizeof(struct bpf_hdr); + + //calculus of data to be copied + //if the user buffer is smaller than data to be copied, + //only some data will be copied + data=&Open->tme.block_data[Open->tme.active_read]; + + if (data->last_read.tv_sec!=0) + data->last_read=header->bh_tstamp; + + + bytecopy=data->block_size*data->filled_blocks; + + if ((IrpSp->Parameters.Read.Length-sizeof(struct bpf_hdr))Parameters.Read.Length-sizeof(struct bpf_hdr))/ data->block_size; + else + bytecopy=data->filled_blocks; + + tmp=data->shared_memory_base_address; + block_size=data->block_size; + + for (cnt=0;cntmachine_lock); + RtlCopyMemory(UserPointer,tmp,block_size); + NdisReleaseSpinLock(&Open->machine_lock); + tmp+=block_size; + UserPointer+=block_size; + } + + bytecopy*=block_size; + + header->bh_caplen=bytecopy; + header->bh_datalen=header->bh_caplen; + + EXIT_SUCCESS(bytecopy+sizeof(struct bpf_hdr)); + } + + if (Open->Bhead == Open->Btail || Open->mode & MODE_DUMP) + // The timeout has expired, but the buffer is still empty (or the packets must be written to file). + // We must awake the application, returning an empty buffer. + { + EXIT_SUCCESS(0); + } + + } + + // + // The buffer if full enough to be copied, + // + NdisAcquireSpinLock( &Open->BufLock ); + + Thead = Open->Bhead; + Ttail = Open->Btail; + TLastByte = Open->BLastByte; + + //get the address of the buffer + CurrBuff=Open->Buffer; + + NdisReleaseSpinLock( &Open->BufLock ); + + Input_Buffer_Length=IrpSp->Parameters.Read.Length; + packp=(PUCHAR)MmGetSystemAddressForMdl(Irp->MdlAddress); + + + // + //fill the application buffer + // + if(Ttail > Thead){ //first of all see if it we can copy all the buffer in one time + if((Ttail-Thead)ReadEvent); + + PacketMoveMem(packp,CurrBuff+Thead,Ttail-Thead,&(Open->Bhead)); + EXIT_SUCCESS(Ttail-Thead); + } + } + else if((TLastByte - Thead) < Input_Buffer_Length){ + PacketMoveMem(packp, CurrBuff+Thead, TLastByte - Thead, &(Open->Bhead)); + + NdisAcquireSpinLock( &Open->BufLock ); + + Open->BLastByte = Open->Btail; + Open->Bhead = 0; + + NdisReleaseSpinLock( &Open->BufLock ); + + EXIT_SUCCESS(TLastByte-Thead); + } + + //the buffer must be scannned to determine the number of bytes to copy + SizeToCopy = 0; + while(TRUE){ + if(Thead + SizeToCopy == Ttail) + break; + + if(Thead + SizeToCopy == TLastByte && TLastByte != Ttail){ + + PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead)); + // Reset the buffer + NdisAcquireSpinLock( &Open->BufLock ); + (INT)Open->BLastByte = -1; + Open->Bhead = 0; + NdisReleaseSpinLock( &Open->BufLock ); + + EXIT_SUCCESS(SizeToCopy); + } + + // Get the size of the next packet in the buffer + PktLen = ((struct bpf_hdr*)(CurrBuff + Thead + SizeToCopy))->bh_caplen + sizeof(struct bpf_hdr); + + // The length is aligned to 32-bit boundary + PktLen = Packet_WORDALIGN(PktLen); + + if(SizeToCopy + PktLen > Input_Buffer_Length) + break; + + SizeToCopy += PktLen; + } + + PacketMoveMem(packp, CurrBuff+Thead, SizeToCopy, &(Open->Bhead)); + EXIT_SUCCESS(SizeToCopy); + +} + +//------------------------------------------------------------------- + +NDIS_STATUS NPF_tap (IN NDIS_HANDLE ProtocolBindingContext,IN NDIS_HANDLE MacReceiveContext, + IN PVOID HeaderBuffer,IN UINT HeaderBufferSize,IN PVOID LookAheadBuffer, + IN UINT LookaheadBufferSize,IN UINT PacketSize) +{ + POPEN_INSTANCE Open; + PNDIS_PACKET pPacketb; + ULONG SizeToTransfer; + NDIS_STATUS Status; + UINT BytesTransfered; + ULONG BufferLength; + PMDL pMdl; + LARGE_INTEGER CapTime; + LARGE_INTEGER TimeFreq; + struct bpf_hdr *header; + PUCHAR CurrBuff; + UINT Thead; + UINT Ttail; + UINT TLastByte; + UINT fres; + UINT maxbufspace; + USHORT NPFHdrSize; + UINT BufOccupation; + BOOLEAN ResetBuff = FALSE; + + IF_VERY_LOUD(DbgPrint("NPF: tap\n");) + IF_VERY_LOUD(DbgPrint("HeaderBufferSize=%d, LookAheadBuffer=%d, LookaheadBufferSize=%d, PacketSize=%d\n", + HeaderBufferSize, + LookAheadBuffer, + LookaheadBufferSize, + PacketSize);) + + Open= (POPEN_INSTANCE)ProtocolBindingContext; + + Open->Received++; // Number of packets received by filter ++ + + BufOccupation = GetBuffOccupation(Open); // Get the full buffer space + + if(((Open->mode&MODE_CAPT)||(Open->mode&MODE_DUMP)) && Open->BufSize - BufOccupation < PacketSize+HeaderBufferSize+sizeof(struct bpf_hdr)){ + // Heuristic that drops the packet also if it possibly fits in the buffer. + // It allows to avoid filtering in critical situations when CPU is very important. + Open->Dropped++; + return NDIS_STATUS_NOT_ACCEPTED; + } + + NdisAcquireSpinLock(&Open->machine_lock); + + // + //Check if the lookahead buffer follows the mac header. + //If the data follow the header (i.e. there is only a buffer) a normal bpf_filter() is + //executed on the packet. + //Otherwise if there are 2 separate buffers (this could be the case of LAN emulation or + //things like this) bpf_filter_with_2_buffers() is executed. + // + if((UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize) + fres=bpf_filter_with_2_buffers((struct bpf_insn*)(Open->bpfprogram), + HeaderBuffer, + LookAheadBuffer, + HeaderBufferSize, + PacketSize+HeaderBufferSize, + LookaheadBufferSize+HeaderBufferSize, + &Open->mem_ex, + &Open->tme, + &G_Start_Time); + + + else + if(Open->Filter != NULL) + { + if (Open->bpfprogram != NULL) + { + fres=Open->Filter->Function(HeaderBuffer, + PacketSize+HeaderBufferSize, + LookaheadBufferSize+HeaderBufferSize); + + // Restore the stack. + // I ignore the reason, but this instruction is needed only at kernel level +#ifndef __GNUC__ + _asm add esp,12 +#else + asm("add $0x12,%esp;"); +#endif + } + else + fres = -1; + } + else + fres=bpf_filter((struct bpf_insn*)(Open->bpfprogram), + HeaderBuffer, + PacketSize+HeaderBufferSize, + LookaheadBufferSize+HeaderBufferSize, + &Open->mem_ex, + &Open->tme, + &G_Start_Time); + + NdisReleaseSpinLock(&Open->machine_lock); + + if(Open->mode==MODE_MON) + // we are in monitor mode + { + if (fres==1) + KeSetEvent(Open->ReadEvent,0,FALSE); + return NDIS_STATUS_NOT_ACCEPTED; + + } + + if(fres==0) + // Packet not accepted by the filter, ignore it. + return NDIS_STATUS_NOT_ACCEPTED; + + //if the filter returns -1 the whole packet must be accepted + if(fres==-1 || fres > PacketSize+HeaderBufferSize)fres=PacketSize+HeaderBufferSize; + + if(Open->mode & MODE_STAT){ + // we are in statistics mode + NdisAcquireSpinLock( &Open->CountersLock ); + + Open->Npackets.QuadPart++; + + if(PacketSize+HeaderBufferSize<60) + Open->Nbytes.QuadPart+=60; + else + Open->Nbytes.QuadPart+=PacketSize+HeaderBufferSize; + // add preamble+SFD+FCS to the packet + // these values must be considered because are not part of the packet received from NDIS + Open->Nbytes.QuadPart+=12; + + NdisReleaseSpinLock( &Open->CountersLock ); + + if(!(Open->mode & MODE_DUMP)){ + return NDIS_STATUS_NOT_ACCEPTED; + } + } + + if(Open->BufSize==0)return NDIS_STATUS_NOT_ACCEPTED; + + if(Open->mode & MODE_DUMP && Open->MaxDumpPacks && (UINT)Open->Accepted > Open->MaxDumpPacks){ + // Reached the max number of packets to save in the dump file. Discard the packet and stop the dump thread. + Open->DumpLimitReached = TRUE; // This stops the thread + // Awake the dump thread + NdisSetEvent(&Open->DumpEvent); + + // Awake the application + KeSetEvent(Open->ReadEvent,0,FALSE); + + return NDIS_STATUS_NOT_ACCEPTED; + } + + // Calculate the correct size for the header associated with the packet + NPFHdrSize=(Open->mode==MODE_CAPT)? sizeof(struct bpf_hdr): sizeof(struct sf_pkthdr); + + NdisAcquireSpinLock( &Open->BufLock ); + + Thead=Open->Bhead; + Ttail=Open->Btail; + TLastByte = Open->BLastByte; + + NdisReleaseSpinLock( &Open->BufLock ); + + maxbufspace=Packet_WORDALIGN(fres+NPFHdrSize); + + if(Ttail+maxbufspace >= Open->BufSize){ + if(Thead <= maxbufspace) + { + Open->Dropped++; + return NDIS_STATUS_NOT_ACCEPTED; + } + else{ + Ttail=0; + ResetBuff = TRUE; + } + } + + if (Thead > Ttail && (Thead-Ttail) <= maxbufspace) + { + Open->Dropped++; + return NDIS_STATUS_NOT_ACCEPTED; + } + + CurrBuff=Open->Buffer+Ttail; + + if(LookaheadBufferSize != PacketSize || (UINT)LookAheadBuffer-(UINT)HeaderBuffer != HeaderBufferSize) + { + // Allocate an MDL to map the portion of the buffer following the header + pMdl=IoAllocateMdl(CurrBuff+HeaderBufferSize+LookaheadBufferSize+NPFHdrSize, + maxbufspace, + FALSE, + FALSE, + NULL); + + if (pMdl == NULL) + { + // Unable to map the memory: packet lost + IF_LOUD(DbgPrint("NPF: Read-Failed to allocate Mdl\n");) + Open->Dropped++; + return NDIS_STATUS_NOT_ACCEPTED; + } + MmBuildMdlForNonPagedPool(pMdl); + + //allocate the packet from NDIS + NdisAllocatePacket(&Status, &pPacketb, Open->PacketPool); + if (Status != NDIS_STATUS_SUCCESS) + { + IF_LOUD(DbgPrint("NPF: Tap - No free packets\n");) + IoFreeMdl(pMdl); + Open->Dropped++; + return NDIS_STATUS_NOT_ACCEPTED; + } + //link the buffer to the packet + NdisChainBufferAtFront(pPacketb,pMdl); + + BufferLength=fres-HeaderBufferSize; + //Find out how much to transfer + SizeToTransfer = (PacketSize < BufferLength) ? PacketSize : BufferLength; + + //copy the ethernet header into buffer + NdisMoveMappedMemory((CurrBuff)+NPFHdrSize,HeaderBuffer,HeaderBufferSize); + + //Copy the look ahead buffer + if(LookaheadBufferSize) + { + NdisMoveMappedMemory((CurrBuff) + NPFHdrSize + HeaderBufferSize, + LookAheadBuffer, + (SizeToTransfer < LookaheadBufferSize)? SizeToTransfer : LookaheadBufferSize ); + + SizeToTransfer = (SizeToTransfer > LookaheadBufferSize)? + SizeToTransfer - LookaheadBufferSize : 0; + } + + Open->TransferMdl=pMdl; + + if(SizeToTransfer) + { + //Call the Mac to transfer the packet + NdisTransferData(&Status, + Open->AdapterHandle, + MacReceiveContext, + LookaheadBufferSize, + SizeToTransfer, + pPacketb, + &BytesTransfered); + } + else{ + BytesTransfered = 0; + } + + } + else + { + // The whole packet is in the lookahead buffer, we can avoid the call to NdisTransferData. + // This allows us to avoid the allocation of the MDL and the NDIS packet as well + RtlCopyMemory((CurrBuff) + NPFHdrSize, + HeaderBuffer, + HeaderBufferSize + LookaheadBufferSize); + + BytesTransfered = 0; + + Open->TransferMdl = NULL; + Status = NDIS_STATUS_SUCCESS; + } + + if (Status != NDIS_STATUS_FAILURE) + { + + Open->Accepted++; // Increase the accepted packets counter + + if( fres > (BytesTransfered+HeaderBufferSize+LookaheadBufferSize) ) + fres = BytesTransfered+HeaderBufferSize+LookaheadBufferSize; + + // + // Build the header + // + header=(struct bpf_hdr*)CurrBuff; + GET_TIME(&header->bh_tstamp,&G_Start_Time); + header->bh_caplen=fres; + header->bh_datalen=PacketSize+HeaderBufferSize; + if(Open->mode==MODE_CAPT){ + header->bh_hdrlen=NPFHdrSize; + // Don't align if the packet goes to disk + Ttail+=Packet_WORDALIGN(fres + NPFHdrSize); + } + else + Ttail+=fres+NPFHdrSize; + + //update the buffer + NdisAcquireSpinLock( &Open->BufLock ); + + if(ResetBuff){ + Open->BLastByte = Open->Btail; + } + Open->Btail=Ttail; + + NdisReleaseSpinLock( &Open->BufLock ); + } + + if (Status != NDIS_STATUS_PENDING){ + + if( Open->TransferMdl != NULL) + // Complete the request and free the buffers + NPF_TransferDataComplete(Open,pPacketb,Status,fres); + else{ + // Unfreeze the consumer + if(GetBuffOccupation(Open)>Open->MinToCopy){ + if(Open->mode & MODE_DUMP){ + NdisSetEvent(&Open->DumpEvent); + } + else + KeSetEvent(Open->ReadEvent,0,FALSE); + } + + } + } + + return NDIS_STATUS_SUCCESS; + +} + +//------------------------------------------------------------------- + +VOID NPF_TransferDataComplete (IN NDIS_HANDLE ProtocolBindingContext,IN PNDIS_PACKET pPacket, + IN NDIS_STATUS Status,IN UINT BytesTransfered) +{ + POPEN_INSTANCE Open; + + IF_LOUD(DbgPrint("NPF: TransferDataComplete\n");) + + Open= (POPEN_INSTANCE)ProtocolBindingContext; + + IoFreeMdl(Open->TransferMdl); + //recylcle the packet + NdisReinitializePacket(pPacket); + //Put the packet on the free queue + NdisFreePacket(pPacket); + // Unfreeze the consumer + if(GetBuffOccupation(Open)>Open->MinToCopy){ + if(Open->mode & MODE_DUMP){ + NdisSetEvent(&Open->DumpEvent); + } + else + KeSetEvent(Open->ReadEvent,0,FALSE); + } + return; +} + +//------------------------------------------------------------------- + +VOID NPF_ReceiveComplete(IN NDIS_HANDLE ProtocolBindingContext) +{ + IF_VERY_LOUD(DbgPrint("NPF: NPF_ReceiveComplete\n");) + return; +}