windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/node.c
2020-09-26 16:20:57 +08:00

766 lines
15 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
node.c
Abstract:
This module contains the Appletalk Node management code.
Author:
Jameel Hyder (jameelh@microsoft.com)
Nikhil Kamkolkar (nikhilk@microsoft.com)
Revision History:
19 Jun 1992 Initial Version
Notes: Tab stop: 4
--*/
#include <atalk.h>
#pragma hdrstop
#define FILENUM NODE
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGEINIT, AtalkInitNodeCreateOnPort)
#pragma alloc_text(PAGEINIT, AtalkInitNodeAllocate)
#pragma alloc_text(PAGEINIT, AtalkInitNodeGetPramAddr)
#pragma alloc_text(PAGEINIT, AtalkInitNodeSavePramAddr)
#endif
ATALK_ERROR
AtalkInitNodeCreateOnPort(
PPORT_DESCRIPTOR pPortDesc,
BOOLEAN AllowStartupRange,
BOOLEAN RouterNode,
PATALK_NODEADDR NodeAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PATALK_NODE pNode;
ATALK_ERROR error = ATALK_NO_ERROR;
ATALK_NODEADDR desiredNode = { UNKNOWN_NETWORK, UNKNOWN_NODE};
PWSTR NodeName;
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
do
{
if ((pPortDesc->pd_Flags & PD_FINDING_NODE) == 0)
{
pPortDesc->pd_Flags |= PD_FINDING_NODE;
}
else
{
// Return if we are already trying to find a node
error = ATALK_NODE_FINDING;
break;
}
// We should not be here if we have already allocated a router node and the user nodes
ASSERT(!RouterNode || ((pPortDesc->pd_Flags & PD_ROUTER_NODE) == 0));
ASSERT ((pPortDesc->pd_Flags & (PD_ROUTER_NODE | PD_USER_NODE_1 | PD_USER_NODE_2))
!= (PD_ROUTER_NODE | PD_USER_NODE_1 | PD_USER_NODE_2));
// On non-extended ports we only allow one node! The theory being that some
// LocalTalk cards are too smart for their own good and have a concept of
// their "source node number" and thus only support one node, also on
// non-extended ports, nodes are scarse.
if (!EXT_NET(pPortDesc))
{
// For a localtalk node we do things differently.
// During initialization time, we would have obtained
// the address from the mac, that will be the node
// address.
ASSERT(pPortDesc->pd_Flags & PD_BOUND);
ASSERT(pPortDesc->pd_AlapNode != 0);
// This needs to be initialized to UNKNOWN_NETWORK or obtained
// from the net during initialization.
ASSERT(pPortDesc->pd_NetworkRange.anr_FirstNetwork == UNKNOWN_NETWORK);
if (!ATALK_SUCCESS((error = AtalkInitNodeAllocate(pPortDesc, &pNode))))
{
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INIT_COULDNOTGETNODE,
0,
NULL,
0);
break;
}
// Use the allocated structure to set the info.
// Thread this into the port structure.
pPortDesc->pd_LtNetwork =
pNode->an_NodeAddr.atn_Network =
pPortDesc->pd_NetworkRange.anr_FirstNetwork;
pNode->an_NodeAddr.atn_Node = (BYTE)pPortDesc->pd_AlapNode;
// Reference the port for this node.
AtalkPortReferenceByPtrNonInterlock(pPortDesc, &error);
if (!ATALK_SUCCESS(error))
{
AtalkFreeMemory(pNode);
break;
}
// Now put it in the port descriptor
pNode->an_Next = pPortDesc->pd_Nodes;
pPortDesc->pd_Nodes = pNode;
}
else
{
// Use PRAM values if we have them
if (RouterNode)
{
NodeName = ROUTER_NODE_VALUE;
if (pPortDesc->pd_RoutersPramNode.atn_Network != UNKNOWN_NETWORK)
{
desiredNode = pPortDesc->pd_RoutersPramNode;
}
}
else
{
if ((pPortDesc->pd_Flags & PD_USER_NODE_1) == 0)
{
NodeName = USER_NODE1_VALUE;
if (pPortDesc->pd_UsersPramNode1.atn_Network != UNKNOWN_NETWORK)
{
// If we are not a router node, and the first user node
// has not been allocated...
desiredNode = pPortDesc->pd_UsersPramNode1;
}
}
else if ((pPortDesc->pd_Flags & PD_USER_NODE_2) == 0)
{
NodeName = USER_NODE2_VALUE;
if (pPortDesc->pd_UsersPramNode2.atn_Network != UNKNOWN_NETWORK)
{
// If we are not a router node, and the second user node
// has not been allocated...
desiredNode = pPortDesc->pd_UsersPramNode2;
}
}
}
// Flags should be set so future get node requests return failure
// until we are done with this attempt. We need to call
// the aarp routines without the lock held - they will
// block.
ASSERT(pPortDesc->pd_Flags & PD_FINDING_NODE);
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// If this routine succeeds in finding the node, it
// will chain in the atalkNode into the port. It also
// returns with the proper flags set/reset in the
// pPortDesc structure. It will also have referenced the port
// and inserted the node into the port's node list.
error = AtalkInitAarpForNodeOnPort(pPortDesc,
AllowStartupRange,
desiredNode,
&pNode);
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (!ATALK_SUCCESS(error))
{
// AARP for node failed.
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_INIT_COULDNOTGETNODE,
0,
NULL,
0);
}
}
} while (FALSE);
// Ok, done finding node. No need for a crit section.
pPortDesc->pd_Flags &= ~PD_FINDING_NODE;
if (ATALK_SUCCESS(error))
{
// If router node, remember it in port descriptor
// Do this before setting up the rtmp/nbp listeners.
// In anycase, clients must check this value for null,
// not guaranteed as zip socket could already be open.
if (RouterNode)
pPortDesc->pd_RouterNode = pNode;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Setup the RTMP, NBP and EP listeners on this node.
// These will be the non-router versions. StartRouting
// calls will then switch them to be the router versions
// at the appropriate time.
error = AtalkInitDdpOpenStaticSockets(pPortDesc, pNode);
if (ATALK_SUCCESS(error))
{
if (EXT_NET(pPortDesc))
{
// We always save this address.
AtalkInitNodeSavePramAddr(pPortDesc,
NodeName,
&pNode->an_NodeAddr);
}
// Return the address of the node opened.
if (NodeAddr != NULL)
*NodeAddr = pNode->an_NodeAddr;
}
else
{
// Error opening sockets. Release node, return failure
LOG_ERRORONPORT(pPortDesc,
EVENT_ATALK_NODE_OPENSOCKETS,
0,
NULL,
0);
AtalkNodeReleaseOnPort(pPortDesc, pNode);
}
}
else
{
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
}
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
("Creation node on port %lx failed! %lx\n",
pPortDesc, error));
}
else
{
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
("Creation node on port %lx with addr %lx.%lx and p%lx\n",
pPortDesc, pNode->an_NodeAddr.atn_Network,
pNode->an_NodeAddr.atn_Node, pNode));
}
return(error);
}
ATALK_ERROR
AtalkNodeReleaseOnPort(
PPORT_DESCRIPTOR pPortDesc,
PATALK_NODE pNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PDDP_ADDROBJ pDdpAddr, pNextAddr;
ATALK_ERROR error;
KIRQL OldIrql;
SHORT i;
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_WARN,
("AtalkNodeReleaseOnPort: Releasing node %lx on port %lx!\n", pNode, pPortDesc));
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
if ((pNode->an_NodeAddr.atn_Network == AtalkUserNode1.atn_Network) &&
(pNode->an_NodeAddr.atn_Node == AtalkUserNode1.atn_Node))
{
pPortDesc->pd_Flags &= ~PD_USER_NODE_1;
AtalkUserNode1.atn_Network = 0;
AtalkUserNode1.atn_Node = 0;
}
else if ((pNode->an_NodeAddr.atn_Network == AtalkUserNode2.atn_Network) &&
(pNode->an_NodeAddr.atn_Node == AtalkUserNode2.atn_Node))
{
pPortDesc->pd_Flags &= ~PD_USER_NODE_2;
AtalkUserNode2.atn_Network = 0;
AtalkUserNode2.atn_Node = 0;
}
if ((pNode->an_Flags & AN_CLOSING) == 0)
{
// Set the closing flag.
pNode->an_Flags |= AN_CLOSING;
// First close all the sockets on the node
for (i = 0; i < NODE_DDPAO_HASH_SIZE; i++)
{
pNextAddr = NULL;
AtalkDdpReferenceNextNc(pNode->an_DdpAoHash[i],
&pDdpAddr,
&error);
if (!ATALK_SUCCESS(error))
{
// Check the other hash table entries. No non-closing
// sockets on this list.
continue;
}
while (TRUE)
{
// Get the next non-closing node using our referenced node before
// closing it. Note we use pDdpAddr->...Flink.
AtalkDdpReferenceNextNc(pDdpAddr->ddpao_Next,
&pNextAddr,
&error);
// Close the referenced ddp addr after releasing the node lock.
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
if (pDdpAddr->ddpao_Flags & DDPAO_SOCK_INTERNAL)
{
AtalkDdpCloseAddress(pDdpAddr, NULL, NULL);
}
else
{
AtalkDdpPnPSuspendAddress(pDdpAddr);
}
// Dereference the address.
AtalkDdpDereference(pDdpAddr);
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
if (pNextAddr != NULL)
pDdpAddr = pNextAddr;
else
break;
}
}
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
// Remove the creation reference for this node.
AtalkNodeDereference(pNode);
}
else
{
// We are already closing.
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
}
return(ATALK_NO_ERROR);
}
BOOLEAN
AtalkNodeExistsOnPort(
PPORT_DESCRIPTOR pPortDesc,
PATALK_NODEADDR pNodeAddr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PATALK_NODE pCheckNode;
BOOLEAN exists = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
for (pCheckNode = pPortDesc->pd_Nodes;
pCheckNode != NULL;
pCheckNode = pCheckNode->an_Next)
{
if (ATALK_NODES_EQUAL(&pCheckNode->an_NodeAddr, pNodeAddr))
{
exists = TRUE;
break;
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
return(exists);
}
VOID
AtalkInitNodeSavePramAddr(
IN PPORT_DESCRIPTOR pPortDesc,
IN PWSTR RegValue,
OUT PATALK_NODEADDR pNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
UNICODE_STRING valueName;
ULONG bytesWritten;
ULONG ValueToSave;
// Save the node value as xxxx00yy where xxxx is network, yy is node
ValueToSave = (pNode->atn_Network << 16) + pNode->atn_Node;
RtlInitUnicodeString (&valueName, RegValue);
ZwSetValueKey(pPortDesc->pd_AdapterInfoHandle,
&valueName,
0,
REG_DWORD,
&ValueToSave,
sizeof(ULONG));
}
VOID
AtalkInitNodeGetPramAddr(
IN PPORT_DESCRIPTOR pPortDesc,
IN PWSTR RegValue,
OUT PATALK_NODEADDR pNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
NTSTATUS Status;
UNICODE_STRING valueName;
ULONG bytesWritten;
ULONG ValueRead;
BYTE buffer[sizeof(KEY_VALUE_FULL_INFORMATION) + 32];
PKEY_VALUE_FULL_INFORMATION nodeValue = (PKEY_VALUE_FULL_INFORMATION)buffer;
RtlInitUnicodeString (&valueName, RegValue);
Status = ZwQueryValueKey(pPortDesc->pd_AdapterInfoHandle,
&valueName,
KeyValueFullInformation,
buffer,
sizeof(buffer),
&bytesWritten);
if (NT_SUCCESS(Status))
{
ValueRead = *(PULONG)(buffer + nodeValue->DataOffset);
}
else
{
ValueRead = 0;
ASSERT (UNKNOWN_NETWORK == 0);
ASSERT (UNKNOWN_NODE == 0);
}
pNode->atn_Node = (BYTE)(ValueRead & 0xFF);
pNode->atn_Network = (USHORT)(ValueRead >> 16);
if ((pNode->atn_Network == UNKNOWN_NETWORK) ||
(pNode->atn_Node == UNKNOWN_NODE))
{
pNode->atn_Node = UNKNOWN_NODE;
pNode->atn_Network = UNKNOWN_NETWORK;
}
}
VOID
AtalkZapPramValue(
IN PPORT_DESCRIPTOR pPortDesc,
IN PWSTR RegValue
)
{
UNICODE_STRING valueName;
ULONG bytesWritten;
ULONG ValueToSave;
// Write 0 to the value to zap it for now.
ValueToSave = 0;
RtlInitUnicodeString (&valueName, RegValue);
ZwSetValueKey(pPortDesc->pd_AdapterInfoHandle,
&valueName,
0,
REG_DWORD,
&ValueToSave,
sizeof(ULONG));
}
ATALK_ERROR
AtalkInitNodeAllocate(
IN PPORT_DESCRIPTOR pPortDesc,
OUT PATALK_NODE *ppNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PATALK_NODE pNode;
// Allocate a new active Node structure
if ((pNode = (PATALK_NODE)AtalkAllocZeroedMemory(sizeof(ATALK_NODE))) == NULL)
{
return(ATALK_RESR_MEM);
}
// Initialize some elements of the structure. Remaining stuff
// done when the node is actually being inserted into the port
// hash table.
#if DBG
pNode->an_Signature = AN_SIGNATURE;
#endif
// Initialize the Nbp Id & Enumerator
pNode->an_NextNbpId = 0;
pNode->an_NextNbpEnum = 0;
pNode->an_NextDynSkt = FIRST_DYNAMIC_SOCKET;
INITIALIZE_SPIN_LOCK(&pNode->an_Lock);
pNode->an_Port = pPortDesc; // Port on which node exists
pNode->an_RefCount = 1; // Reference for creation.
// Return pointer to allocated node
*ppNode = pNode;
return(ATALK_NO_ERROR);
}
VOID
AtalkNodeRefByAddr(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR NodeAddr,
OUT PATALK_NODE * ppNode,
OUT PATALK_ERROR pErr
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PATALK_NODE pNode;
KIRQL OldIrql;
BOOLEAN foundNode = FALSE;
*pErr = ATALK_NODE_NONEXISTENT;
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
for (pNode = pPortDesc->pd_Nodes; pNode != NULL; pNode = pNode->an_Next)
{
ASSERT(VALID_ATALK_NODE(pNode));
// Note: On non-extended ports, there should be only one pNode.
if (((NodeAddr->atn_Network == CABLEWIDE_BROADCAST_NETWORK) ||
(pNode->an_NodeAddr.atn_Network == NodeAddr->atn_Network) ||
(!EXT_NET(pPortDesc) && (pNode->an_NodeAddr.atn_Network == UNKNOWN_NETWORK)))
&&
((NodeAddr->atn_Node == ATALK_BROADCAST_NODE) ||
(pNode->an_NodeAddr.atn_Node == NodeAddr->atn_Node)))
{
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_INFO,
("AtalkNodeRefByAddr: Found: %lx.%lx for Lookup: %lx.%lx\n",
pNode->an_NodeAddr.atn_Network, pNode->an_NodeAddr.atn_Node,
NodeAddr->atn_Network, NodeAddr->atn_Node));
foundNode = TRUE;
break;
}
}
if (foundNode)
{
AtalkNodeRefByPtr(pNode, pErr);
// Return a pointer to the referenced node.
if (ATALK_SUCCESS(*pErr))
{
ASSERT(ppNode != NULL);
ASSERT(pNode != NULL);
*ppNode = pNode;
}
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
}
VOID
AtalkNodeRefNextNc(
IN PATALK_NODE pNode,
IN PATALK_NODE * ppNode,
OUT PATALK_ERROR pErr
)
/*++
Routine Description:
MUST BE CALLED WITH THE PORTLOCK HELD!
Arguments:
Return Value:
--*/
{
*pErr = ATALK_FAILURE;
*ppNode = NULL;
for (; pNode != NULL; pNode = pNode->an_Next)
{
ASSERT(VALID_ATALK_NODE(pNode));
AtalkNodeRefByPtr(pNode, pErr);
if (ATALK_SUCCESS(*pErr))
{
// Ok, this node is referenced!
*ppNode = pNode;
break;
}
}
}
VOID
AtalkNodeDeref(
IN OUT PATALK_NODE pNode
)
/*++
Routine Description:
Arguments:
Return Value:
--*/
{
PPORT_DESCRIPTOR pPortDesc = pNode->an_Port;
KIRQL OldIrql;
BOOLEAN done = FALSE;
ASSERT(VALID_ATALK_NODE(pNode));
ACQUIRE_SPIN_LOCK(&pNode->an_Lock, &OldIrql);
ASSERT(pNode->an_RefCount > 0);
if (--pNode->an_RefCount == 0)
{
done = TRUE;
}
RELEASE_SPIN_LOCK(&pNode->an_Lock, OldIrql);
if (done)
{
PATALK_NODE *ppNode;
ASSERT((pNode->an_Flags & AN_CLOSING) != 0);
DBGPRINT(DBG_COMP_NODE, DBG_LEVEL_WARN,
("AtalkNodeDeref: Freeing node %lx\n", pNode));
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// Remove this guy from the port linkage
for (ppNode = &pNode->an_Port->pd_Nodes;
*ppNode != NULL;
ppNode = &((*ppNode)->an_Next))
{
if (*ppNode == pNode)
{
*ppNode = pNode->an_Next;
break;
}
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Dereference the port for this node
AtalkPortDereference(pPortDesc);
// Free the node structure
AtalkFreeMemory(pNode);
}
}