windows-nt/Source/XPSP1/NT/net/nwlink/fwd/netbios.c
2020-09-26 16:20:57 +08:00

361 lines
10 KiB
C

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
ntos\tdi\isn\fwd\netbios.c
Abstract:
Netbios packet processing
Author:
Vadim Eydelman
Revision History:
--*/
#include "precomp.h"
LIST_ENTRY NetbiosQueue;
KSPIN_LOCK NetbiosQueueLock;
WORK_QUEUE_ITEM NetbiosWorker;
BOOLEAN NetbiosWorkerScheduled=FALSE;
ULONG NetbiosPacketsQuota;
ULONG MaxNetbiosPacketsQueued = DEF_MAX_NETBIOS_PACKETS_QUEUED;
/*++
*******************************************************************
P r o c e s s N e t b i o s Q u e u e
Routine Description:
Process packets in the netbios broadcast queue (sends them on
all interfaces in sequence)
Arguments:
Context - unused
Return Value:
None
*******************************************************************
--*/
VOID
ProcessNetbiosQueue (
PVOID Context
) {
KIRQL oldIRQL;
LIST_ENTRY tempQueue;
KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL);
// Check if there is something in the queue
if (!IsListEmpty (&NetbiosQueue)) {
// Move the queue to local variable
InsertHeadList (&NetbiosQueue, &tempQueue);
RemoveEntryList (&NetbiosQueue);
InitializeListHead (&NetbiosQueue);
KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL);
do {
PLIST_ENTRY cur;
PPACKET_TAG pktTag;
cur = RemoveHeadList (&tempQueue);
pktTag = CONTAINING_RECORD (cur, PACKET_TAG, PT_QueueLink);
// Check if this packet has to be sent on other
// interfaces
if (!(pktTag->PT_Flags&PT_NB_DESTROY)) {
PINTERFACE_CB dstIf = pktTag->PT_InterfaceReference;
PUCHAR dataPtr = pktTag->PT_Data;
UINT rtCount = *(dataPtr+IPXH_XPORTCTL);
PUCHAR netListPtr;
UINT i;
if (dstIf==NULL) {
// This is a brand new packet: not sent on any
// interface yet
USHORT dstSock = GETUSHORT (dataPtr+IPXH_DESTSOCK);
// Check if we have a static route for this name
// (offset to name depends on packet dest socket)
if (dstSock==IPX_NETBIOS_SOCKET)
dstIf = FindNBDestination (dataPtr+NB_NAME);
else if (dstSock==IPX_SMB_NAME_SOCKET)
dstIf = FindNBDestination (dataPtr+SMB_NAME);
else
dstIf = NULL;
if (dstIf!=NULL) {
// Static route found, make sure this packet
// won't be sent on any other interface
pktTag->PT_Flags |= PT_NB_DESTROY;
InterlockedIncrement (&NetbiosPacketsQuota);
// Make sure the packet has not traversed
// this network already
for (i=0, netListPtr=dataPtr+IPXH_HDRSIZE; i<rtCount; i++,netListPtr+=4) {
if (GETULONG (netListPtr)==dstIf->ICB_Network)
break;
}
// Make sure we are allowed to send on this
// interface
if ((dstIf!=InternalInterface)
&& (i==rtCount) // Has not already traversed
// this network
&& IS_IF_ENABLED (dstIf)
&& ((dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL)
|| (dstIf->ICB_NetbiosDeliver
==FWD_NB_DELIVER_STATIC)
|| ((dstIf->ICB_NetbiosDeliver
==FWD_NB_DELIVER_IF_UP)
&& (dstIf->ICB_Stats.OperationalState
==FWD_OPER_STATE_UP)))) {
NOTHING;
}
else {
// We have static route, but can't send it,
// no point to propagate in on other interfaces
// as well
ReleaseInterfaceReference (dstIf);
dstIf = NULL;
goto FreePacket;
}
}
else { // no static route
goto FindNextInterface;
}
}
else { // not a brand new packet (already sent on some
// interfaces)
FindNextInterface:
// Loop through the interface list till we find
// the one on which we can send
while ((dstIf=GetNextInterfaceReference (dstIf))!=NULL) {
// Check if we allowed to send on this interface
if (IS_IF_ENABLED (dstIf)
&& ((dstIf->ICB_NetbiosDeliver==FWD_NB_DELIVER_ALL)
|| ((dstIf->ICB_NetbiosDeliver
==FWD_NB_DELIVER_IF_UP)
&& (dstIf->ICB_Stats.OperationalState
==FWD_OPER_STATE_UP)))) {
// Make sure the packet has not traversed
// this network already
for (i=0, netListPtr=dataPtr+IPXH_HDRSIZE; i<rtCount; i++,netListPtr+=4) {
if (GETULONG (netListPtr)==dstIf->ICB_Network)
break;
}
// Network was not in the list
if (i==rtCount)
break;
}
}
}
// Save the destination interface in the packet
pktTag->PT_InterfaceReference = dstIf;
// Go ahead and send if we have a valid destination
if (dstIf!=NULL) {
SendPacket (dstIf, pktTag);
// The rest does not apply: if the packet was sent or
// failed, it will be queued back to NetbiosQueue;
// if it was queued to interface to be connected,
// a copy of it will be queued to NetbiosQueue
continue;
}
// else no more destinations to send this packet on
}
else { // Packet has to be destroyed
if (pktTag->PT_InterfaceReference!=NULL)
ReleaseInterfaceReference (pktTag->PT_InterfaceReference);
}
FreePacket:
IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION,
("IpxFwd: No more interfaces for nb packet %08lx.\n",
pktTag));
if (MeasuringPerformance
&& (pktTag->PT_PerfCounter!=0)) {
LARGE_INTEGER PerfCounter = KeQueryPerformanceCounter (NULL);
PerfCounter.QuadPart -= pktTag->PT_PerfCounter;
KeAcquireSpinLock (&PerfCounterLock, &oldIRQL);
PerfBlock.TotalNbPacketProcessingTime += PerfCounter.QuadPart;
PerfBlock.NbPacketCounter += 1;
if (PerfBlock.MaxNbPacketProcessingTime < PerfCounter.QuadPart)
PerfBlock.MaxNbPacketProcessingTime = PerfCounter.QuadPart;
KeReleaseSpinLock (&PerfCounterLock, oldIRQL);
}
if (!(pktTag->PT_Flags&PT_NB_DESTROY))
InterlockedIncrement (&NetbiosPacketsQuota);
FreePacket (pktTag);
} while (!IsListEmpty (&tempQueue));
KeAcquireSpinLock (&NetbiosQueueLock, &oldIRQL);
if (IsListEmpty (&NetbiosQueue)
|| !EnterForwarder ()) {
NetbiosWorkerScheduled = FALSE;
}
else {
ExQueueWorkItem (&NetbiosWorker, DelayedWorkQueue);
}
}
KeReleaseSpinLock (&NetbiosQueueLock, oldIRQL);
LeaveForwarder ();
}
/*++
*******************************************************************
P r o c e s s N e t b i o s P a c k e t
Routine Description:
Processes received netbios broadcast packet (checks network list
and source filter, updates input statistics)
Arguments:
srcIf - interfae on which packet was received
pktTag - netbios packet
Return Value:
None
*******************************************************************
--*/
VOID
ProcessNetbiosPacket (
PINTERFACE_CB srcIf,
PPACKET_TAG pktTag
) {
PUCHAR dataPtr, dataPtr2;
UINT rtCount;
UINT i, j;
KIRQL oldIRQL;
ULONG ulNetwork;
dataPtr = pktTag->PT_Data;
rtCount = *(dataPtr+IPXH_XPORTCTL);
//
// pmay: 264339: Make sure valid networks are listed
//
// We used to only verify that the source network was not
// contained in the list. Now, we verify that 0 and 0xfffffff
// are also absent.
//
for (i = 0, dataPtr += IPXH_HDRSIZE; i < rtCount; i++, dataPtr += 4)
{
ulNetwork = GETULONG (dataPtr);
if ((srcIf->ICB_Network == ulNetwork) ||
(ulNetwork == 0xffffffff) ||
(ulNetwork == 0x0))
{
break;
}
}
//
// pmay: 272193
//
// nwlnknb on nt4 puts status bits into the first router slot in
// the type 20 payload. As a consequence, the router has to
// ignore the first slot when validating the router table so that nt4
// client bcasts aren't dropped at the router.
//
if (rtCount == 0)
{
j = 1;
dataPtr2 = dataPtr + 4;
}
else
{
j = i;
dataPtr2 = dataPtr;
}
//
// pmay: 264331
//
// Make sure the rest of the networks listed are zero
//
for (; j < 8; j++, dataPtr2 += 4)
{
ulNetwork = GETULONG (dataPtr2);
if (ulNetwork != 0x0)
{
break;
}
}
// We scaned the whole list and we haven't found it
if ((i == rtCount) && (j == 8)) {
FILTER_ACTION action;
action = FltFilter (pktTag->PT_Data,
GETUSHORT (pktTag->PT_Data+IPXH_LENGTH),
srcIf->ICB_FilterInContext, NULL);
// Apply the input filter
if (action==FILTER_PERMIT) {
InterlockedIncrement (&srcIf->ICB_Stats.NetbiosReceived);
InterlockedIncrement (&srcIf->ICB_Stats.InDelivers);
PUTULONG (srcIf->ICB_Network, dataPtr);
*(pktTag->PT_Data+IPXH_XPORTCTL) += 1;
IPX_NODE_CPY (pktTag->PT_Target.MacAddress, BROADCAST_NODE);
// Initialize the packet
pktTag->PT_InterfaceReference = NULL; // not yet sent on any
// interfaces
pktTag->PT_Flags = 0; // No flags
QueueNetbiosPacket (pktTag);
IpxFwdDbgPrint (DBG_NETBIOS, DBG_INFORMATION,
("IpxFwd: Queued nb packet %08lx from if %ld.\n",
pktTag, srcIf->ICB_Index));
}
else {
ASSERT (action==FILTER_DENY_IN);
IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING,
("IpxFwd: Filtered out nb packet %08lx"
" from if %ld.\n", pktTag, srcIf->ICB_Index));
InterlockedIncrement (&NetbiosPacketsQuota);
InterlockedIncrement (&srcIf->ICB_Stats.InFiltered);
FreePacket (pktTag);
}
}
else {
IpxFwdDbgPrint (DBG_NETBIOS, DBG_WARNING,
("IpxFwd: Source net is already in nb packet %08lx"
" from if %ld.\n", pktTag, srcIf->ICB_Index));
InterlockedIncrement (&NetbiosPacketsQuota);
InterlockedIncrement (&srcIf->ICB_Stats.InDiscards);
FreePacket (pktTag);
}
ReleaseInterfaceReference (srcIf);
}
/*++
*******************************************************************
D e l e t e N e t b i o s Q u e u e
Routine Description:
Deletes the netbios bradcast queue
Arguments:
None
Return Value:
None
*******************************************************************
--*/
VOID
DeleteNetbiosQueue (
void
) {
while (!IsListEmpty (&NetbiosQueue)) {
PPACKET_TAG pktTag = CONTAINING_RECORD (NetbiosQueue.Flink,
PACKET_TAG,
PT_QueueLink);
RemoveEntryList (&pktTag->PT_QueueLink);
if (pktTag->PT_InterfaceReference!=NULL) {
ReleaseInterfaceReference (pktTag->PT_InterfaceReference);
}
FreePacket (pktTag);
}
}