BOOLEAN NPF_IsPacketSelfSent( _In_ PNET_BUFFER_LIST pNetBufferList, _In_ BOOLEAN bIPv4 ) { NTSTATUS status = STATUS_SUCCESS; NET_BUFFER* pNetBuffer = 0; PVOID pContiguousData = NULL; UCHAR pPacketData[IPV6_HDR_LEN]; UCHAR iProtocol; TRACE_ENTER(); pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pNetBufferList); while (pNetBuffer) { pContiguousData = NdisGetDataBuffer(pNetBuffer, bIPv4 ? IP_HDR_LEN : IPV6_HDR_LEN, pPacketData, 1, 0); if (!pContiguousData) { status = STATUS_UNSUCCESSFUL; TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_IsPacketSelfSent: NdisGetDataBuffer() [status: %#x]\n", status); TRACE_EXIT(); return FALSE; } else { iProtocol = bIPv4 ? ((PIP_HEADER) pContiguousData)->ip_Protocol : ((PIP6_HEADER) pContiguousData)->ip6_CTL.ip6_HeaderCtl.ip6_NextHeader; if (iProtocol == IPPROTO_NPCAP_LOOPBACK) { TRACE_EXIT(); return TRUE; } else { TRACE_EXIT(); return FALSE; } } pNetBuffer = pNetBuffer->Next; } TRACE_EXIT(); return FALSE; }
static __inline uint32_t OvsGetTcpPayloadLength(PNET_BUFFER_LIST nbl) { IPHdr *ipHdr; char *ipBuf[sizeof(IPHdr)]; PNET_BUFFER curNb; curNb = NET_BUFFER_LIST_FIRST_NB(nbl); ipHdr = NdisGetDataBuffer(curNb, sizeof *ipHdr, (PVOID) &ipBuf, 1 /*no align*/, 0); TCPHdr *tcp = (TCPHdr *)((PCHAR)ipHdr + ipHdr->ihl * 4); return (UINT16)ntohs(ipHdr->tot_len) - (ipHdr->ihl * 4) - (sizeof * tcp); }
OVS_ETHERNET_HEADER* ReadEthernetHeaderOnly(_In_ NET_BUFFER* net_buffer) { OVS_ETHERNET_HEADER* buffer = NULL; ULONG bufferSize = sizeof(OVS_ETHERNET_HEADER); buffer = (OVS_ETHERNET_HEADER*)NdisGetDataBuffer(net_buffer, bufferSize, NULL, 1, 0); //the buffer of a NET_BUFFER MUST have the ethernet header in contiguous space (according to msdn doc), //i.e. the var buffer must be != NULL here. OVS_CHECK(buffer); if (!buffer) { return NULL; } if (buffer->type == OVS_ETHERTYPE_QTAG) { bufferSize = sizeof(OVS_ETHERNET_HEADER_TAGGED); buffer = NdisGetDataBuffer(net_buffer, bufferSize, NULL, 1, 0); OVS_CHECK(buffer); } return buffer; }
ULONG tapGetNetBufferFrameType(__in PNET_BUFFER NetBuffer) /*++ Routine Description: Reads the network frame's destination address to determine the type (broadcast, multicast, etc) Runs at IRQL <= DISPATCH_LEVEL. Arguments: NetBuffer The NB to examine Return Value: NDIS_PACKET_TYPE_BROADCAST NDIS_PACKET_TYPE_MULTICAST NDIS_PACKET_TYPE_DIRECTED --*/ { PETH_HEADER ethernetHeader; ethernetHeader = (PETH_HEADER)NdisGetDataBuffer(NetBuffer, sizeof(ETH_HEADER), NULL, 1, 0); ASSERT(ethernetHeader); if (ETH_IS_BROADCAST(ethernetHeader->dest)) { return NDIS_PACKET_TYPE_BROADCAST; } else if (ETH_IS_MULTICAST(ethernetHeader->dest)) { return NDIS_PACKET_TYPE_MULTICAST; } else { return NDIS_PACKET_TYPE_DIRECTED; } }
// Process the received packet void NeoWrite(void *buf) { UINT num, i, size; UCHAR *packet_buf; NET_BUFFER_LIST *nbl_chain = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_nbl_chain = 0; // Validate arguments if (buf == NULL) { return; } // Number of packets num = NEO_NUM_PACKET(buf); if (num > NEO_MAX_PACKET_EXCHANGE) { // Number of packets is too many return; } if (num == 0) { // No packet return; } if (ctx->Halting != FALSE) { // Stopping return; } if (ctx->Paused) { // Paused return; } if (ctx->Opened == FALSE) { // Not connected return; } for (i = 0;i < num;i++) { PACKET_BUFFER *p = ctx->PacketBuffer[i]; void *dst; NET_BUFFER_LIST *nbl = ctx->PacketBuffer[i]->NetBufferList; NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); nbl->SourceHandle = ctx->NdisMiniport; NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; size = NEO_SIZE_OF_PACKET(buf, i); if (size > NEO_MAX_PACKET_SIZE) { size = NEO_MAX_PACKET_SIZE; } if (size < NEO_PACKET_HEADER_SIZE) { size = NEO_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)(NEO_ADDR_OF_PACKET(buf, i)); if (OK(NdisRetreatNetBufferDataStart(nb, size, 0, NULL))) { // Buffer copy dst = NdisGetDataBuffer(nb, size, NULL, 1, 0); if (dst != NULL) { NeoCopy(dst, packet_buf, size); if (nbl_chain == NULL) { nbl_chain = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; num_nbl_chain++; } } nbl->Status = NDIS_STATUS_RESOURCES; ctx->Status.Int64BytesRecvTotal += (UINT64)size; if (packet_buf[0] & 0x40) { ctx->Status.Int64NumRecvBroadcast++; ctx->Status.Int64BytesRecvBroadcast += (UINT64)size; } else { ctx->Status.Int64NumRecvUnicast++; ctx->Status.Int64BytesRecvUnicast += (UINT64)size; } } if (nbl_chain == NULL) { return; } // Notify that it has received ctx->Status.NumPacketRecv += num_nbl_chain; NdisMIndicateReceiveNetBufferLists(ctx->NdisMiniport, nbl_chain, 0, num_nbl_chain, NDIS_RECEIVE_FLAGS_RESOURCES); if (true) { // Restore the packet buffer NET_BUFFER_LIST *nbl = nbl_chain; while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); if (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); NdisAdvanceNetBufferDataStart(nb, size, false, NULL); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } } }
// checks if the NBL chain contains any ICMP Echo Replies BOOLEAN ProcessNblChain ( PNET_BUFFER_LIST NetBufferLists ) { PNET_BUFFER_LIST NetBufferList = NetBufferLists; PNET_BUFFER_LIST NextNetBufferList; // Step #1 : Iterate through all the NET_BUFFER_LISTs in the NET_BUFFER_LIST chain for (NetBufferList = NetBufferLists; NetBufferList; NetBufferList = NET_BUFFER_LIST_NEXT_NBL(NetBufferList) ) { PNET_BUFFER NetBuffer; // Step #2 : Iterate through all the NET_BUFFERs in the NET_BUFFER_LISTs passed to the function for (NetBuffer = NET_BUFFER_LIST_FIRST_NB(NetBufferList); NetBuffer; NetBuffer = NET_BUFFER_NEXT_NB(NetBuffer)) { // Declare a storage buffer big enough to hold the MAC header (ETH_HEADER), // IP Header (IP_HEADER) and ICMP Header (ICMP_HEADER) UCHAR LocalBuffer[sizeof(ETH_HEADER) + sizeof(IP_HEADER) + sizeof(ICMP_HEADER)]; ULONG LocalBufferLength = sizeof(ETH_HEADER) + sizeof(IP_HEADER) + sizeof(ICMP_HEADER); PUCHAR HeaderBuffer = NULL; PETH_HEADER EthHeader; PIP_HEADER IpHeader; PICMP_HEADER IcmpHeader; // Step #4 : Attempt to retrieve the pointer (NdisGetDataBuffer()) to the data area in HeaderBuffer // from the NB that contains the ETH, IP and ICMP headers. Pass in the storage buffer (LocalBuffer) // where the data would be copied in case the ETH, IP and ICMP headers are not contiguous. if (NULL == (HeaderBuffer = NdisGetDataBuffer(NetBuffer, LocalBufferLength, LocalBuffer, 1, 1))) { // Step #3 : If the current NB is not large enough (NET_BUFFER_DATA_LENGTH()) to hold the required headers then skip this NB. continue; } // Step #5 : Initialize EthHeader to the point to the start of HeaderBuffer EthHeader = (PETH_HEADER)HeaderBuffer; // Step #6 : If the layer 3 protocol (h_proto) in the ETH_HEADER is not IPv4 (ETH_TYPE_IP) then skip this NB. // Note h_proto is in network byte format and must be converted to host byte format (ntohs()) if (ETH_TYPE_IP != ntohs(EthHeader->h_proto)) { continue; } // Step #7 : Initialize IpHeader to point to HeaderBuffer after ETH_HEADER IpHeader = (PIP_HEADER)((ULONG_PTR)EthHeader + sizeof(*EthHeader)); // Step #8 : If the layer 4 protocol (ip_p) in the PIP_HEADER is not ICMP (IPPROTO_ICMP) then skip this NB if (IPPROTO_ICMP != IpHeader->ip_p) { continue; } // Step #9 : Initialize IcmpHeader to point to HeaderBuffer after ETH_HEADER and IP_HEADER IcmpHeader = (PICMP_HEADER)((ULONG_PTR)IpHeader + sizeof(*IpHeader)); DPF (( "%s ICMP (%u) %u.%u.%u.%u < %u.%u.%u.%u\n", __FUNCTION__, IcmpHeader->type, ((PUCHAR)(&IpHeader->ip_dst))[0], ((PUCHAR)(&IpHeader->ip_dst))[1], ((PUCHAR)(&IpHeader->ip_dst))[2], ((PUCHAR)(&IpHeader->ip_dst))[3], ((PUCHAR)(&IpHeader->ip_src))[0], ((PUCHAR)(&IpHeader->ip_src))[1], ((PUCHAR)(&IpHeader->ip_src))[2], ((PUCHAR)(&IpHeader->ip_src))[3] )); // Step #10 : If the IMCP type (type) in the ICMP_HEADER is echo reply (ICMP_ECHOREPLY) // then return TRUE which will cause the caller to drop the packet if (ICMP_ECHOREPLY == IcmpHeader->type) { return TRUE; } } } return FALSE; } // ProcessNblChain()
NTSTATUS InsertNBs( _Inout_ KKDRV_QUEUE_DATA *queueData, _In_ NET_BUFFER_LIST *head ) { NTSTATUS status = STATUS_SUCCESS; KLOCK_QUEUE_HANDLE lockHandle; NET_BUFFER_LIST *nbl = head; NET_BUFFER *nb; while (nbl) { nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb) { PVOID data; ULONG dataLength = NET_BUFFER_DATA_LENGTH(nb); PKKDRV_PACKET packet = (PKKDRV_PACKET)ExAllocatePoolWithTag( NonPagedPool, KKDRV_PACKET_SIZE + dataLength, KKDRV_TAG ); if (packet == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }; packet->dataLength = dataLength; data = NdisGetDataBuffer(nb, dataLength, NULL, 1, 0); if (data == NULL) { NdisGetDataBuffer(nb, dataLength, &packet->data, 1, 0); } else { RtlCopyMemory(&(packet->data), data, dataLength); } KeAcquireInStackQueuedSpinLockAtDpcLevel( &queueData->queueLock, &lockHandle ); InsertTailList(&queueData->queue, &packet->entry); queueData->queueLength++; if (queueData->queueLength > queueData->queueLengthMax) { PLIST_ENTRY entry = RemoveHeadList(&queueData->queue); ExFreePoolWithTag(entry, KKDRV_TAG); queueData->queueLength--; } KeReleaseInStackQueuedSpinLockFromDpcLevel( &lockHandle ); nb = nb->Next; } nbl = nbl->Next; } return status; }
void NPF_NetworkClassify( _In_ const FWPS_INCOMING_VALUES* inFixedValues, _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, _Inout_opt_ void* layerData, _In_ const FWPS_FILTER* filter, _In_ UINT64 flowContext, _Inout_ FWPS_CLASSIFY_OUT* classifyOut ) #endif { POPEN_INSTANCE GroupOpen; POPEN_INSTANCE TempOpen; NTSTATUS status = STATUS_SUCCESS; UINT32 ipHeaderSize = 0; UINT32 bytesRetreated = 0; UINT32 bytesRetreatedEthernet = 0; INT32 iIPv4 = -1; INT32 iDrection = -1; BOOLEAN bSelfSent = FALSE; PVOID pContiguousData = NULL; NET_BUFFER* pNetBuffer = 0; UCHAR pPacketData[ETHER_HDR_LEN]; PNET_BUFFER_LIST pNetBufferList = (NET_BUFFER_LIST*) layerData; COMPARTMENT_ID compartmentID = UNSPECIFIED_COMPARTMENT_ID; FWPS_PACKET_INJECTION_STATE injectionState = FWPS_PACKET_INJECTION_STATE_MAX; #if(NTDDI_VERSION >= NTDDI_WIN7) UNREFERENCED_PARAMETER(classifyContext); #endif UNREFERENCED_PARAMETER(filter); UNREFERENCED_PARAMETER(flowContext); // Make the default action. if (classifyOut->rights & FWPS_RIGHT_ACTION_WRITE) classifyOut->actionType = FWP_ACTION_CONTINUE; #if(NTDDI_VERSION >= NTDDI_WIN7) // Filter out fragment packets and reassembled packets. if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_FRAGMENT_DATA) { return; } if (inMetaValues->currentMetadataValues & FWP_CONDITION_FLAG_IS_REASSEMBLED) { return; } #endif TRACE_ENTER(); // Get the packet protocol (IPv4 or IPv6) and the direction (Inbound or Outbound). if (inFixedValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || inFixedValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4) { iIPv4 = 1; } else // if (inFixedValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6 || inFixedValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) { iIPv4 = 0; } if (inFixedValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V4 || inFixedValues->layerId == FWPS_LAYER_OUTBOUND_IPPACKET_V6) { iDrection = 0; } else // if (inFixedValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V4 || inFixedValues->layerId == FWPS_LAYER_INBOUND_IPPACKET_V6) { iDrection = 1; } if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_IP_HEADER_SIZE) { ipHeaderSize = inMetaValues->ipHeaderSize; } injectionState = FwpsQueryPacketInjectionState(iIPv4 ? g_InjectionHandle_IPv4 : g_InjectionHandle_IPv6, pNetBufferList, NULL); if (injectionState == FWPS_PACKET_INJECTED_BY_SELF || injectionState == FWPS_PACKET_PREVIOUSLY_INJECTED_BY_SELF) { TRACE_MESSAGE(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: this packet is injected by ourself, let it go\n"); TRACE_EXIT(); return; } // Inbound: Initial offset is at the Transport Header, so retreat the size of the Ethernet Header and IP Header. // Outbound: Initial offset is at the IP Header, so just retreat the size of the Ethernet Header. // We retreated the packet in two phases: 1) retreat the IP Header (if has), 2) clone the packet and retreat the Ethernet Header. // We must NOT retreat the Ethernet Header on the original packet, or this will lead to BAD_POOL_CALLER Bluescreen. bytesRetreated = iDrection ? ipHeaderSize : 0; status = NdisRetreatNetBufferListDataStart(pNetBufferList, bytesRetreated, 0, NULL, NULL); if (status != STATUS_SUCCESS) { TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: NdisRetreatNetBufferListDataStart(bytesRetreated) [status: %#x]\n", status); TRACE_EXIT(); return; } //bSelfSent = NPF_IsPacketSelfSent(pNetBufferList, (BOOLEAN)iIPv4); bSelfSent = (iDrection == 0) ? FALSE : NPF_IsPacketSelfSent(pNetBufferList, (BOOLEAN) iIPv4); TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: NPF_IsPacketSelfSent() [bSelfSent: %#x]\n", bSelfSent); if (bSelfSent) { NdisAdvanceNetBufferListDataStart(pNetBufferList, iIPv4 ? IP_HDR_LEN : IPV6_HDR_LEN, FALSE, 0); } // Here if this NBL is sent by ourself, we will clone it starting from IP header and inject it into Network Layer send path. if (bSelfSent) { PNET_BUFFER_LIST pClonedNetBufferList_Injection; status = FwpsAllocateCloneNetBufferList(pNetBufferList, NULL, NULL, 0, &pClonedNetBufferList_Injection); if (status != STATUS_SUCCESS) { TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: FwpsAllocateCloneNetBufferList(pClonedNetBufferList_Injection) [status: %#x]\n", status); goto Exit_WSK_IP_Retreated; } if (FWPS_IS_METADATA_FIELD_PRESENT(inMetaValues, FWPS_METADATA_FIELD_COMPARTMENT_ID)) compartmentID = (COMPARTMENT_ID)inMetaValues->compartmentId; // This cloned NBL will be freed in NPF_NetworkInjectionComplete function. status = FwpsInjectNetworkSendAsync(iIPv4 ? g_InjectionHandle_IPv4 : g_InjectionHandle_IPv6, NULL, 0, compartmentID, pClonedNetBufferList_Injection, NPF_NetworkInjectionComplete, NULL); if (status != STATUS_SUCCESS) { TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: FwpsInjectNetworkSendAsync() [status: %#x]\n", status); FwpsFreeCloneNetBufferList(pClonedNetBufferList_Injection, 0); goto Exit_WSK_IP_Retreated; } // We have successfully re-inject the cloned NBL, so remove this one. classifyOut->actionType = FWP_ACTION_BLOCK; classifyOut->flags |= FWPS_CLASSIFY_OUT_FLAG_ABSORB; classifyOut->rights ^= FWPS_RIGHT_ACTION_WRITE; } // We clone this NBL again, for packet reading operation. PNET_BUFFER_LIST pClonedNetBufferList; status = FwpsAllocateCloneNetBufferList(pNetBufferList, NULL, NULL, 0, &pClonedNetBufferList); if (status != STATUS_SUCCESS) { TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: FwpsAllocateCloneNetBufferList() [status: %#x]\n", status); goto Exit_WSK_IP_Retreated; } bytesRetreatedEthernet = g_DltNullMode ? DLT_NULL_HDR_LEN : ETHER_HDR_LEN; status = NdisRetreatNetBufferListDataStart(pClonedNetBufferList, bytesRetreatedEthernet, 0, 0, 0); if (status != STATUS_SUCCESS) { bytesRetreatedEthernet = 0; TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: NdisRetreatNetBufferListDataStart(bytesRetreatedEthernet) [status: %#x]\n", status); goto Exit_Packet_Cloned; } pNetBuffer = NET_BUFFER_LIST_FIRST_NB(pClonedNetBufferList); while (pNetBuffer) { pContiguousData = NdisGetDataBuffer(pNetBuffer, bytesRetreatedEthernet, pPacketData, 1, 0); if (!pContiguousData) { status = STATUS_UNSUCCESSFUL; TRACE_MESSAGE1(PACKET_DEBUG_LOUD, "NPF_NetworkClassify: NdisGetDataBuffer() [status: %#x]\n", status); goto Exit_Ethernet_Retreated; } else { if (g_DltNullMode) { ((PDLT_NULL_HEADER) pContiguousData)->null_type = iIPv4 ? DLTNULLTYPE_IP : DLTNULLTYPE_IPV6; } else { RtlZeroMemory(pContiguousData, ETHER_ADDR_LEN * 2); ((PETHER_HEADER) pContiguousData)->ether_type = iIPv4 ? RtlUshortByteSwap(ETHERTYPE_IP) : RtlUshortByteSwap(ETHERTYPE_IPV6); } } pNetBuffer = pNetBuffer->Next; } // Send the loopback packets data to the user-mode code. if (g_LoopbackOpenGroupHead) { //get the 1st group adapter child GroupOpen = g_LoopbackOpenGroupHead->GroupNext; } else { // Should not come here GroupOpen = NULL; } while (GroupOpen != NULL) { TempOpen = GroupOpen; if (TempOpen->AdapterBindingStatus == ADAPTER_BOUND) { //let every group adapter receive the packets NPF_TapExForEachOpen(TempOpen, pClonedNetBufferList); } GroupOpen = TempOpen->GroupNext; } Exit_Ethernet_Retreated: // Advance the offset back to the original position. NdisAdvanceNetBufferListDataStart(pClonedNetBufferList, bytesRetreatedEthernet, FALSE, 0); Exit_Packet_Cloned: FwpsFreeCloneNetBufferList(pClonedNetBufferList, 0); Exit_WSK_IP_Retreated: if (bSelfSent) { status = NdisRetreatNetBufferListDataStart(pNetBufferList, iIPv4 ? IP_HDR_LEN : IPV6_HDR_LEN, 0, NULL, NULL); // if (status != STATUS_SUCCESS) // { // TRACE_MESSAGE1(PACKET_DEBUG_LOUD, // "NPF_NetworkClassify: NdisRetreatNetBufferListDataStart(IP_HDR_LEN) [status: %#x]\n", // status); // // goto Exit_IP_Retreated; // } } /*Exit_IP_Retreated:*/ NdisAdvanceNetBufferListDataStart(pNetBufferList, bytesRetreated, FALSE, 0); // // print "protocol, direction, fragment, reassembled" info for the current packet. // // int iFragment = -1; // if (inMetaValues->currentMetadataValues & FWPS_METADATA_FIELD_FRAGMENT_DATA) // { // iFragment = 1; // } // else // { // iFragment = 0; // } // // int iReassembled = -1; // if (inMetaValues->currentMetadataValues & FWP_CONDITION_FLAG_IS_REASSEMBLED) // { // iReassembled = 1; // } // else // { // iReassembled = 0; // } // IF_LOUD(DbgPrint("\n\nNPF_NetworkClassify: Loopback packet found !!! protocol=[%d] (ipv4=0, ipv6=1), direction=[%d] (out=0, in=1), fragment=[%d], reassembled=[%d]\n", iProtocol, iDrection, iFragment, iReassembled);) TRACE_EXIT(); return; }
// Packet send handler void NeoNdisSendNetBufferLists(NDIS_HANDLE MiniportAdapterContext, NET_BUFFER_LIST *NetBufferLists, NDIS_PORT_NUMBER PortNumber, ULONG SendFlags) { if (ctx == NULL) { return; } // Update the connection state NeoCheckConnectState(); if (NeoNdisSendPacketsHaltCheck(NetBufferLists) == FALSE) { // Device is stopped return; } // Operation of the packet queue NeoLockPacketQueue(); { NET_BUFFER_LIST *nbl; if (NeoNdisSendPacketsHaltCheck(NetBufferLists) == FALSE) { // Device is stopped NeoUnlockPacketQueue(); return; } nbl = NetBufferLists; while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); while (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); if (size >= NEO_MIN_PACKET_SIZE && size <= NEO_MAX_PACKET_SIZE) { UCHAR *buf = NeoMalloc(size); void *ptr; ptr = NdisGetDataBuffer(nb, size, buf, 1, 0); if (ptr == NULL) { ctx->Status.NumPacketSendError++; ctx->Status.Int64NumSendError++; NeoFree(buf); } else { if (ptr != buf) { NeoCopy(buf, ptr, size); } NeoInsertQueue(buf, size); ctx->Status.NumPacketSend++; if (buf[0] & 0x40) { ctx->Status.Int64NumSendBroadcast++; ctx->Status.Int64BytesSendBroadcast += (UINT64)size; } else { ctx->Status.Int64NumSendUnicast++; ctx->Status.Int64BytesSendUnicast += (UINT64)size; } ctx->Status.Int64BytesSendTotal += (UINT64)size; } } else { ctx->Status.NumPacketSendError++; ctx->Status.Int64NumSendError++; } nb = NET_BUFFER_NEXT_NB(nb); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } } NeoUnlockPacketQueue(); // Notify the transmission completion NdisMSendNetBufferListsComplete(ctx->NdisMiniport, NetBufferLists, NDIS_STATUS_SUCCESS); // Reception event NeoSet(ctx->Event); }
// NDIS packet reception notification procedure void SlNdisReceiveNetBufferListsProc(NDIS_HANDLE protocol_binding_context, NET_BUFFER_LIST *net_buffer_lists, NDIS_PORT_NUMBER port_number, ULONG NumberOfNetBufferLists, ULONG receive_flags) { SL_ADAPTER *a = (SL_ADAPTER *)protocol_binding_context; UINT i; UINT return_flags = 0; NET_BUFFER_LIST *nbl; UCHAR *tmp_buffer; UINT tmp_size; if (net_buffer_lists == NULL || NumberOfNetBufferLists == 0) { return; } if (a->AdapterHandle2 == NULL) { a->AdapterHandle2 = a->AdapterHandle; } if (NDIS_TEST_RECEIVE_AT_DISPATCH_LEVEL(receive_flags)) { NDIS_SET_RETURN_FLAG(return_flags, NDIS_RETURN_FLAGS_DISPATCH_LEVEL); } if (a->Halt || a->Device == NULL || a->Device->Halting || a->Ready == false || a->AdapterHandle == NULL) { goto LABEL_CLEANUP; } tmp_buffer = a->TmpBuffer; tmp_size = sizeof(a->TmpBuffer); nbl = net_buffer_lists; SlLockList(a->Device->FileList); { if (a->Halt == false) { for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { // Lock the receive queue SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); SlLock(f->RecvLock); } while (nbl != NULL) { NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); bool is_vlan = false; UCHAR vlan_tag[2]; if (NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo) != 0) { NDIS_NET_BUFFER_LIST_8021Q_INFO qinfo; qinfo.Value = NET_BUFFER_LIST_INFO(nbl, Ieee8021QNetBufferListInfo); if (qinfo.TagHeader.VlanId != 0) { USHORT tag_us; is_vlan = true; tag_us = (qinfo.TagHeader.UserPriority & 0x07 << 13) | (qinfo.TagHeader.CanonicalFormatId & 0x01 << 12) | (qinfo.TagHeader.VlanId & 0x0FFF); vlan_tag[0] = ((UCHAR *)(&tag_us))[1]; vlan_tag[1] = ((UCHAR *)(&tag_us))[0]; } } while (nb != NULL) { UINT size = NET_BUFFER_DATA_LENGTH(nb); if (size >= 14 && size <= tmp_size && size <= (UINT)((is_vlan == false) ? SL_MAX_PACKET_SIZE : (SL_MAX_PACKET_SIZE - 4))) { UCHAR *ptr = NdisGetDataBuffer(nb, size, tmp_buffer, 1, 0); if (ptr != NULL) { // Insert the queue to all waiting files for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); if (f->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); if (is_vlan == false) { // Normal packet SlCopy(q->Data, ptr, size); q->Size = size; } else { // Insert a tag in the case of IEEE802.1Q packet SlCopy(q->Data, ptr, 12); q->Data[12] = 0x81; q->Data[13] = 0x00; SlCopy(&q->Data[14], vlan_tag, 2); SlCopy(&q->Data[16], &ptr[12], size - 12); q->Size = size + 4; } q->Next = NULL; if (f->RecvPacketHead == NULL) { f->RecvPacketHead = q; } else { f->RecvPacketTail->Next = q; } f->RecvPacketTail = q; f->NumRecvPackets++; } } } } nb = NET_BUFFER_NEXT_NB(nb); } nbl = NET_BUFFER_LIST_NEXT_NBL(nbl); } // Hit the event for (i = 0;i < SL_LIST_NUM(a->Device->FileList);i++) { SL_FILE *f = SL_LIST_DATA(a->Device->FileList, i); // Unlock the receive queue SlUnlock(f->RecvLock); SlSet(f->Event); } } } SlUnlockList(a->Device->FileList); LABEL_CLEANUP: if (NDIS_TEST_RECEIVE_CAN_PEND(receive_flags)) { NdisReturnNetBufferLists(a->AdapterHandle2, net_buffer_lists, return_flags); } }
// Write procedure of the device NTSTATUS SlDeviceWriteProc(DEVICE_OBJECT *device_object, IRP *irp) { SL_DEVICE *dev = *((SL_DEVICE **)device_object->DeviceExtension); NTSTATUS ret = STATUS_UNSUCCESSFUL; IO_STACK_LOCATION *irp_stack = IoGetCurrentIrpStackLocation(irp); UINT ret_size = 0; if (dev->IsBasicDevice == false) { // Adapter device SL_FILE *f = irp_stack->FileObject->FsContext; if (irp_stack->Parameters.Write.Length == SL_EXCHANGE_BUFFER_SIZE) { UCHAR *buf = irp->UserBuffer; if (dev->Halting || dev->Adapter->Halt || buf == NULL) { // Halting } else { // Write the packet MDL *mdl; UINT num = SL_NUM_PACKET(buf); mdl = IoAllocateMdl(buf, SL_EXCHANGE_BUFFER_SIZE, false, false, NULL); if (mdl != NULL) { MmProbeAndLockPages(mdl, KernelMode, IoReadAccess); } ret = true; ret_size = SL_EXCHANGE_BUFFER_SIZE; if (num >= 1 && num <= SL_MAX_PACKET_EXCHANGE) { UINT i, j; NET_BUFFER_LIST *nbl_head = NULL; NET_BUFFER_LIST *nbl_tail = NULL; UINT num_packets = 0; NDIS_HANDLE adapter_handle = NULL; SlLock(f->Adapter->Lock); if (f->Adapter->NumPendingSendPackets <= SL_MAX_PACKET_QUEUED) { // Admit to send only if the number of packets being transmitted does not exceed the specified limit adapter_handle = f->Adapter->AdapterHandle; } if (adapter_handle != NULL) { // Lock the file list which opens the same adapter SlLockList(dev->FileList); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Lock the receive queue of other file lists SlLock(other->RecvLock); other->SetEventFlag = false; } } for (i = 0;i < num;i++) { UINT packet_size = SL_SIZE_OF_PACKET(buf, i); UCHAR *packet_buf; NET_BUFFER_LIST *nbl = NULL; bool ok = false; if (packet_size > SL_MAX_PACKET_SIZE) { packet_size = SL_MAX_PACKET_SIZE; } else if (packet_size < SL_PACKET_HEADER_SIZE) { packet_size = SL_PACKET_HEADER_SIZE; } packet_buf = (UCHAR *)SL_ADDR_OF_PACKET(buf, i); for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Insert into the receive queue of the other file lists if (other->NumRecvPackets < SL_MAX_PACKET_QUEUED) { SL_PACKET *q = SlMalloc(sizeof(SL_PACKET)); SlCopy(q->Data, packet_buf, packet_size); q->Size = packet_size; q->Next = NULL; if (other->RecvPacketHead == NULL) { other->RecvPacketHead = q; } else { other->RecvPacketTail->Next = q; } other->RecvPacketTail = q; other->NumRecvPackets++; other->SetEventFlag = true; } } } // Allocate a new NET_BUFFER_LIST if (f->NetBufferListPool != NULL) { nbl = NdisAllocateNetBufferList(f->NetBufferListPool, 16, 0); if (nbl != NULL) { nbl->SourceHandle = adapter_handle; } } if (nbl != NULL) { // Get the NET_BUFFER from the NET_BUFFER_LIST NET_BUFFER *nb = NET_BUFFER_LIST_FIRST_NB(nbl); NET_BUFFER_LIST_NEXT_NBL(nbl) = NULL; if (nb != NULL && OK(NdisRetreatNetBufferDataStart(nb, packet_size, 0, NULL))) { // Buffer copy UCHAR *dst = NdisGetDataBuffer(nb, packet_size, NULL, 1, 0); if (dst != NULL) { SlCopy(dst, packet_buf, packet_size); ok = true; } else { NdisAdvanceNetBufferDataStart(nb, packet_size, false, NULL); } } } if (ok == false) { if (nbl != NULL) { NdisFreeNetBufferList(nbl); } } else { if (nbl_head == NULL) { nbl_head = nbl; } if (nbl_tail != NULL) { NET_BUFFER_LIST_NEXT_NBL(nbl_tail) = nbl; } nbl_tail = nbl; *((void **)NET_BUFFER_LIST_CONTEXT_DATA_START(nbl)) = f; num_packets++; } } for (j = 0;j < SL_LIST_NUM(dev->FileList);j++) { SL_FILE *other = SL_LIST_DATA(dev->FileList, j); if (other != f) { // Release the receive queue of other file lists SlUnlock(other->RecvLock); // Set an event if (other->SetEventFlag) { SlSet(other->Event); } } } SlUnlockList(dev->FileList); if (nbl_head != NULL) { InterlockedExchangeAdd(&f->NumSendingPacketets, num_packets); InterlockedExchangeAdd(&f->Adapter->NumPendingSendPackets, num_packets); SlUnlock(f->Adapter->Lock); NdisSendNetBufferLists(adapter_handle, nbl_head, 0, 0); } else { SlUnlock(f->Adapter->Lock); } } else { SlUnlock(f->Adapter->Lock); } } if (mdl != NULL) { MmUnlockPages(mdl); IoFreeMdl(mdl); } } } } irp->IoStatus.Information = ret_size; irp->IoStatus.Status = ret; IoCompleteRequest(irp, IO_NO_INCREMENT); return ret; }