windows-nt/Source/XPSP1/NT/net/sfm/atalk/sys/zip.c

3226 lines
80 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
zip.c
Abstract:
This module contains
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 ZIP
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, AtalkZipInit)
#pragma alloc_text(PAGEINIT, AtalkInitZipStartProcessingOnPort)
#pragma alloc_text(PAGEINIT, atalkZipGetZoneListForPort)
#pragma alloc_text(PAGE_RTR, AtalkZipPacketInRouter)
#pragma alloc_text(PAGE_RTR, atalkZipHandleNetInfo)
#pragma alloc_text(PAGE_RTR, atalkZipHandleReply)
#pragma alloc_text(PAGE_RTR, atalkZipHandleQuery)
#pragma alloc_text(PAGE_RTR, atalkZipHandleAtpRequest)
#pragma alloc_text(PAGE_RTR, atalkZipQueryTimer)
#pragma alloc_text(PAGE_NZ, AtalkZipGetMyZone)
#pragma alloc_text(PAGE_NZ, atalkZipGetMyZoneReply)
#pragma alloc_text(PAGE_NZ, AtalkZipGetZoneList)
#pragma alloc_text(PAGE_NZ, atalkZipGetZoneListReply)
#pragma alloc_text(PAGE_NZ, atalkZipZoneInfoTimer)
#pragma alloc_text(PAGE_NZ, atalkZipSendPacket)
#endif
/*** AtalkZipInit
*
*/
ATALK_ERROR
AtalkZipInit(
IN BOOLEAN Init
)
{
if (Init)
{
// Allocate space for zones
AtalkZonesTable = (PZONE *)AtalkAllocZeroedMemory(sizeof(PZONE) * NUM_ZONES_HASH_BUCKETS);
if (AtalkZonesTable == NULL)
{
return ATALK_RESR_MEM;
}
INITIALIZE_SPIN_LOCK(&AtalkZoneLock);
}
else
{
// At this point, we are unloading and there are no race conditions
// or lock contentions. Do not bother locking down the zones table
if (AtalkDesiredZone != NULL)
AtalkZoneDereference(AtalkDesiredZone);
if (AtalkZonesTable != NULL)
{
AtalkFreeMemory(AtalkZonesTable);
AtalkZonesTable = NULL;
}
}
return ATALK_NO_ERROR;
}
/*** AtalkZipStartProcessingOnPort
*
*/
BOOLEAN
AtalkInitZipStartProcessingOnPort(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR pRouterNode
)
{
ATALK_ADDR closeAddr;
ATALK_ERROR Status;
KIRQL OldIrql;
BOOLEAN RetCode = FALSE;
PDDP_ADDROBJ pZpDdpAddr=NULL;
// Switch the incoming zip handler to the router version
closeAddr.ata_Network = pRouterNode->atn_Network;
closeAddr.ata_Node = pRouterNode->atn_Node;
closeAddr.ata_Socket = ZONESINFORMATION_SOCKET;
do
{
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pPortDesc->pd_Flags |= PD_ROUTER_STARTING;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
// Close the non-router version of the handler and start the router version
AtalkDdpInitCloseAddress(pPortDesc, &closeAddr);
if (!ATALK_SUCCESS(Status = AtalkDdpOpenAddress(pPortDesc,
ZONESINFORMATION_SOCKET,
pRouterNode,
AtalkZipPacketInRouter,
NULL,
DDPPROTO_ANY,
NULL,
&pZpDdpAddr)))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkZipStartProcessingOnPort: AtalkDdpOpenAddress failed %ld\n",
Status));
break;
}
// mark the fact that this is an "internal" socket
pZpDdpAddr->ddpao_Flags |= DDPAO_SOCK_INTERNAL;
// Try to get or set the zone information
if (!atalkZipGetZoneListForPort(pPortDesc))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("AtalkZipStartProcessingOnPort: Failed to get zone list for port\n"));
break;
}
if (!atalkZipQryTmrRunning)
{
AtalkTimerInitialize(&atalkZipQTimer,
atalkZipQueryTimer,
ZIP_QUERY_TIMER);
AtalkTimerScheduleEvent(&atalkZipQTimer);
atalkZipQryTmrRunning = TRUE;
}
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pPortDesc->pd_Flags &= ~PD_ROUTER_STARTING;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
RetCode = TRUE;
} while (FALSE);
return RetCode;
}
/*** AtalkZipPacketIn
*
*/
VOID
AtalkZipPacketIn(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR Status,
IN BYTE DdpType,
IN PVOID pHandlerCtx,
IN BOOLEAN OptimizedPath,
IN PVOID OptimizeCtx
)
{
BYTE CmdType, Flags;
BYTE ZoneLen, DefZoneLen, MulticastAddrLen;
PBYTE pZone, pDefZone, pMulticastAddr;
TIME TimeS, TimeE, TimeD;
ULONG Index;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
TimeS = KeQueryPerformanceCounter(NULL);
do
{
if ((Status == ATALK_SOCKET_CLOSED) ||
(DdpType != DDPPROTO_ZIP))
break;
else if (Status != ATALK_NO_ERROR)
{
break;
}
if (!EXT_NET(pPortDesc))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
if (PktLen < ZIP_CMD_OFF+1)
{
break;
}
CmdType = pPkt[ZIP_CMD_OFF];
// We only care about Zip Notifies and NetInfo replies
if (((CmdType != ZIP_NOTIFY) && (CmdType != ZIP_NETINFO_REPLY)) ||
(PktLen < (ZIP_ZONELEN_OFF + 1)))
{
break;
}
// If it is a NetInfoReply, then we should be looking for either the
// default or the desired zone
if ((CmdType != ZIP_NETINFO_REPLY) &&
(pPortDesc->pd_Flags & (PD_FINDING_DEFAULT_ZONE | PD_FINDING_DESIRED_ZONE)))
break;
if ((CmdType == ZIP_NETINFO_REPLY) &&
!(pPortDesc->pd_Flags & (PD_FINDING_DEFAULT_ZONE | PD_FINDING_DESIRED_ZONE)))
break;
// If it is a Notify then the desired zone must be valid
if ((CmdType == ZIP_NOTIFY) &&
!(pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE))
break;
// We have a NetInfoReply or a Notify. Handle it
Flags = pPkt[ZIP_FLAGS_OFF];
Index = ZIP_ZONELEN_OFF;
ZoneLen = pPkt[ZIP_ZONELEN_OFF];
Index ++;
if ((ZoneLen > MAX_ZONE_LENGTH) || (PktLen < (Index + ZoneLen)))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
pZone = pPkt+Index;
Index += ZoneLen;
// If we are looking for a desired zone and we get a late default zone
// response then toss this packet
if ((CmdType == ZIP_NETINFO_REPLY) && (ZoneLen == 0) &&
(pPortDesc->pd_Flags & (PD_FINDING_DESIRED_ZONE)) &&
(pPortDesc->pd_InitialDesiredZone != NULL))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("AtalkZipPacketIn: dropping a NetInfoReply packet\n"));
break;
}
// If we're requesting the zone name, make sure the response matches
// our request. ZoneLen will be zero when we're looking for the def
// zone, so we won't do this test
if ((CmdType == ZIP_NETINFO_REPLY) &&
(ZoneLen != 0) &&
(pPortDesc->pd_InitialDesiredZone != NULL))
{
BOOLEAN NoMatch = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
ASSERT(!(pPortDesc->pd_Flags & PD_ROUTER_RUNNING) ||
(pPortDesc->pd_Flags & PD_FINDING_DESIRED_ZONE));
if (!AtalkFixedCompareCaseInsensitive(pZone,
ZoneLen,
pPortDesc->pd_InitialDesiredZone->zn_Zone,
pPortDesc->pd_InitialDesiredZone->zn_ZoneLen))
{
NoMatch = TRUE;
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (NoMatch)
break;
}
// If its a Notify, make sure we're in the zone that is being changed
if (CmdType == ZIP_NOTIFY)
{
BOOLEAN NoMatch = FALSE;
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (!AtalkFixedCompareCaseInsensitive(pZone, ZoneLen,
pPortDesc->pd_DesiredZone->zn_Zone,
pPortDesc->pd_DesiredZone->zn_ZoneLen))
{
NoMatch = TRUE;
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (NoMatch)
break;
}
if (PktLen < (Index + 1))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
MulticastAddrLen = pPkt[Index++];
if (MulticastAddrLen != pPortDesc->pd_BroadcastAddrLen)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
if (PktLen < (Index + MulticastAddrLen))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
pMulticastAddr = pPkt + Index;
Index += MulticastAddrLen;
#if 0
if (Flags & ZIP_USE_BROADCAST_FLAG)
pMulticastAddr = pPortDesc->pd_BroadcastAddr;
#endif
// Grab second name, if needed or present
DefZoneLen = 0;
if ((CmdType == ZIP_NOTIFY) || (PktLen > Index))
{
if (PktLen < (Index+1))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
DefZoneLen = pPkt[Index++];
if ((DefZoneLen == 0) ||
(DefZoneLen > MAX_ZONE_LENGTH) ||
(PktLen < (Index+DefZoneLen)))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
pDefZone = pPkt+Index;
Index += DefZoneLen;
}
// Make default zone be the new one. We may not have a default/new
// zone in netinfo reply case and we requested for the correct zone
if (DefZoneLen == 0)
{
pDefZone = pZone;
DefZoneLen = ZoneLen;
}
// Make sure the port lock is released before calling any depend/ddp
// etc. routines.
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// If we're just looking for the default zone, set here and note
// our mission completed
if ((pPortDesc->pd_Flags & PD_FINDING_DEFAULT_ZONE) &&
(ZoneLen == 0))
{
if (pPortDesc->pd_DefaultZone != NULL)
AtalkZoneDereference(pPortDesc->pd_DefaultZone);
pPortDesc->pd_DefaultZone = AtalkZoneReferenceByName(pDefZone, DefZoneLen);
if (pPortDesc->pd_DefaultZone == NULL)
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
RES_LOG_ERROR();
break;
}
pPortDesc->pd_Flags |= PD_VALID_DEFAULT_ZONE;
pPortDesc->pd_Flags &= ~PD_FINDING_DEFAULT_ZONE;
}
// Now we want to accept all of the information about 'thiszone'
// for the nodes on the current port
// If the new multicast address is different, remove the old and
// set the new. Don't allow changes to the 'broadcast' multicast
// address.
if (pPortDesc->pd_Flags & PD_FINDING_DESIRED_ZONE)
{
if (!AtalkFixedCompareCaseSensitive(pMulticastAddr,
MulticastAddrLen,
pPortDesc->pd_ZoneMulticastAddr,
MulticastAddrLen))
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
(*pPortDesc->pd_RemoveMulticastAddr)(pPortDesc,
pMulticastAddr,
FALSE,
NULL,
NULL);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
if (!AtalkFixedCompareCaseSensitive(pMulticastAddr,
MulticastAddrLen,
pPortDesc->pd_BroadcastAddr,
MulticastAddrLen))
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
(*pPortDesc->pd_AddMulticastAddr)(pPortDesc,
pMulticastAddr,
FALSE,
NULL,
NULL);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
RtlCopyMemory(pPortDesc->pd_ZoneMulticastAddr,
pMulticastAddr,
MulticastAddrLen);
}
// Finally set this cable range if this is a net info reply
if (CmdType == ZIP_NETINFO_REPLY)
{
GETSHORT2SHORT(&pPortDesc->pd_NetworkRange.anr_FirstNetwork,
pPkt+ZIP_CABLE_RANGE_START_OFF);
GETSHORT2SHORT(&pPortDesc->pd_NetworkRange.anr_LastNetwork,
pPkt+ZIP_CABLE_RANGE_END_OFF);
if (!(pPortDesc->pd_Flags & PD_ROUTER_STARTING))
{
pPortDesc->pd_ARouter.atn_Network = pSrcAddr->ata_Network;
pPortDesc->pd_ARouter.atn_Node = pSrcAddr->ata_Node;
}
pPortDesc->pd_Flags |= PD_SEEN_ROUTER_RECENTLY;
KeSetEvent(&pPortDesc->pd_SeenRouterEvent, IO_NETWORK_INCREMENT, FALSE);
}
// Now that we know the zone
if (pPortDesc->pd_Flags & PD_FINDING_DESIRED_ZONE)
{
pPortDesc->pd_Flags &= ~PD_FINDING_DESIRED_ZONE;
pPortDesc->pd_Flags |= PD_VALID_DESIRED_ZONE;
if (pPortDesc->pd_DesiredZone != NULL)
AtalkZoneDereference(pPortDesc->pd_DesiredZone);
pPortDesc->pd_DesiredZone = AtalkZoneReferenceByName(pDefZone, DefZoneLen);
if (pPortDesc->pd_DesiredZone == NULL)
{
pPortDesc->pd_Flags &= ~PD_VALID_DESIRED_ZONE;
RES_LOG_ERROR();
}
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_ZipPacketInProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumZipPacketsIn,
&AtalkStatsLock.SpinLock);
} while (FALSE);
}
/*** AtalkZipPacketInRouter
*
*/
VOID
AtalkZipPacketInRouter(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR Status,
IN BYTE DdpType,
IN PVOID pHandlerCtx,
IN BOOLEAN OptimizedPath,
IN PVOID OptimizeCtx
)
{
BYTE CmdType;
TIME TimeS, TimeE, TimeD;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
TimeS = KeQueryPerformanceCounter(NULL);
do
{
if (Status == ATALK_SOCKET_CLOSED)
break;
else if (Status != ATALK_NO_ERROR)
{
break;
}
if (DdpType == DDPPROTO_ZIP)
{
if (PktLen < ZIP_FIRST_NET_OFF)
{
break;
}
CmdType = pPkt[ZIP_CMD_OFF];
switch (CmdType)
{
case ZIP_NETINFO_REPLY:
case ZIP_NOTIFY:
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipPacketInRouter: Ignoring %s\n",
(CmdType == ZIP_NOTIFY) ? "Notify" : "NetInfoReply"));
break;
case ZIP_GET_NETINFO:
// We do not want to do a thing if we're starting up
if (pPortDesc->pd_Flags & PD_ROUTER_STARTING)
break;
if (!EXT_NET(pPortDesc))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
if (pPortDesc->pd_ZoneList == NULL)
break; // Not fully up yet !
if (PktLen < ZIP_REQ_ZONENAME_OFF)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipPacketInRouter: GetNetInfo Port %Z\n",
&pPortDesc->pd_AdapterKey));
atalkZipHandleNetInfo(pPortDesc,
pDdpAddr,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
case ZIP_EXT_REPLY:
case ZIP_REPLY:
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipPacketInRouter: %sReply Port %Z\n",
(CmdType == ZIP_REPLY) ? "" : "Extended",
&pPortDesc->pd_AdapterKey));
atalkZipHandleReply(pDdpAddr, pSrcAddr, pPkt, PktLen);
break;
case ZIP_QUERY:
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipPacketInRouter: Query Port %Z\n",
&pPortDesc->pd_AdapterKey));
// We do not want to do a thing if we're starting up
if (pPortDesc->pd_Flags & PD_ROUTER_STARTING)
break;
atalkZipHandleQuery(pPortDesc, pDdpAddr, pSrcAddr, pPkt, PktLen);
break;
default:
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
}
else if (DdpType == DDPPROTO_ATP)
{
USHORT TrId, StartIndex;
if (PktLen < ATP_ZIP_START_INDEX_OFF+1)
{
ASSERT(0);
break;
}
// We do not want to do a thing if we're starting up
if (pPortDesc->pd_Flags & PD_ROUTER_STARTING)
break;
// This had better be a GetZoneList, a GetMyZone ATP request
if ((pPkt[ATP_CMD_CONTROL_OFF] & ATP_FUNC_MASK) != ATP_REQUEST)
break;
if (pPkt[ATP_BITMAP_OFF] != 1)
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
GETSHORT2SHORT(&TrId, pPkt + ATP_TRANS_ID_OFF);
CmdType = pPkt[ATP_ZIP_CMD_OFF];
if ((CmdType != ZIP_GET_ZONE_LIST) &&
(CmdType != ZIP_GET_MY_ZONE) &&
(CmdType != ZIP_GET_LOCAL_ZONES))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("ZIP: Received atp type command %d\n", CmdType));
// Get start index. Not meaningful for GetMyZone
GETSHORT2SHORT(&StartIndex, pPkt+ATP_ZIP_START_INDEX_OFF);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipPacketInRouter: AtpRequest %d, Port %Z\n",
CmdType, &pPortDesc->pd_AdapterKey));
atalkZipHandleAtpRequest(pPortDesc, pDdpAddr, pSrcAddr,
CmdType, TrId, StartIndex);
}
} while (FALSE);
TimeE = KeQueryPerformanceCounter(NULL);
TimeD.QuadPart = TimeE.QuadPart - TimeS.QuadPart;
INTERLOCKED_ADD_LARGE_INTGR_DPC(
&pPortDesc->pd_PortStats.prtst_ZipPacketInProcessTime,
TimeD,
&AtalkStatsLock.SpinLock);
INTERLOCKED_INCREMENT_LONG_DPC(
&pPortDesc->pd_PortStats.prtst_NumZipPacketsIn,
&AtalkStatsLock.SpinLock);
}
/*** atalkZipHandleNetInfo
*
*/
VOID
atalkZipHandleNetInfo(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN PBYTE pPkt,
IN USHORT PktLen
)
{
PBUFFER_DESC pBuffDesc;
BYTE ZoneLen;
PBYTE Datagram, pZoneName;
ATALK_ADDR SrcAddr = *pSrcAddr;
ATALK_ERROR error;
BOOLEAN UseDefZone = FALSE;
USHORT index;
SEND_COMPL_INFO SendInfo;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
do
{
// Get the zone name out of the request
ZoneLen = pPkt[ZIP_REQ_ZONELEN_OFF];
if ((ZoneLen > MAX_ZONE_LENGTH) ||
(PktLen < (USHORT)(ZoneLen + ZIP_REQ_ZONENAME_OFF)))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
pZoneName = pPkt+ZIP_REQ_ZONENAME_OFF;
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
MAX_DGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
break;
}
Datagram = pBuffDesc->bd_CharBuffer;
// Format a GetNetInfo reply command
Datagram[ZIP_CMD_OFF] = ZIP_NETINFO_REPLY;
Datagram[ZIP_FLAGS_OFF] = 0;
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if ((ZoneLen == 0) ||
!AtalkZoneNameOnList(pZoneName, ZoneLen, pPortDesc->pd_ZoneList))
{
Datagram[ZIP_FLAGS_OFF] |= ZIP_ZONE_INVALID_FLAG;
UseDefZone = TRUE;
}
if (AtalkZoneNumOnList(pPortDesc->pd_ZoneList) == 1)
Datagram[ZIP_FLAGS_OFF] |= ZIP_ONLYONE_ZONE_FLAG;
// Add our cable range
PUTSHORT2SHORT(&Datagram[ZIP_FIRST_NET_OFF],
pPortDesc->pd_NetworkRange.anr_FirstNetwork);
PUTSHORT2SHORT(Datagram +ZIP_LAST_NET_OFF,
pPortDesc->pd_NetworkRange.anr_LastNetwork);
// Echo back the requested zone name
Datagram[ZIP_REQ_ZONELEN_OFF] = ZoneLen;
RtlCopyMemory(Datagram+ZIP_REQ_ZONENAME_OFF, pZoneName, ZoneLen);
index = ZIP_REQ_ZONENAME_OFF + ZoneLen;
// Place in the correct zone multicast address
Datagram[index++] = (BYTE)(pPortDesc->pd_BroadcastAddrLen);
if (UseDefZone)
{
pZoneName = pPortDesc->pd_DefaultZone->zn_Zone;
ZoneLen = pPortDesc->pd_DefaultZone->zn_ZoneLen;
}
AtalkZipMulticastAddrForZone(pPortDesc, pZoneName, ZoneLen, Datagram + index);
index += pPortDesc->pd_BroadcastAddrLen;
// If we need it, add in the default zone
if (UseDefZone)
{
Datagram[index++] = ZoneLen = pPortDesc->pd_DefaultZone->zn_ZoneLen;
RtlCopyMemory(Datagram + index, pPortDesc->pd_DefaultZone->zn_Zone, ZoneLen);
index += ZoneLen;
}
// If the request came as a cable-wide broadcast and its
// source network is not valid for this port, then we want
// to respond to cable-wide broadcast rather than the source
if ((pDstAddr->ata_Network == CABLEWIDE_BROADCAST_NETWORK) &&
(pDstAddr->ata_Node == ATALK_BROADCAST_NODE) &&
!WITHIN_NETWORK_RANGE(pSrcAddr->ata_Network,
&pPortDesc->pd_NetworkRange) &&
!WITHIN_NETWORK_RANGE(pSrcAddr->ata_Network,
&AtalkStartupNetworkRange))
{
SrcAddr.ata_Network = CABLEWIDE_BROADCAST_NETWORK;
SrcAddr.ata_Node = ATALK_BROADCAST_NODE;
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, index);
// Finally, send this out
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleNetInfo: Sending Reply to %d.%d.%d\n",
SrcAddr.ata_Network, SrcAddr.ata_Node, SrcAddr.ata_Socket));
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
SendInfo.sc_Ctx1 = pBuffDesc;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
error = AtalkDdpSend(pDdpAddr,
&SrcAddr,
DDPPROTO_ZIP,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(error))
{
AtalkFreeBuffDesc(pBuffDesc);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleNetInfo: AtalkDdpSend %ld\n", error));
}
} while (FALSE);
}
/*** atalkZipHandleReply
*
*/
VOID
atalkZipHandleReply(
IN PDDP_ADDROBJ pDdpAddr,
IN PATALK_ADDR pSrcAddr,
IN PBYTE pPkt,
IN USHORT PktLen
)
{
ULONG index, TotalNetCnt;
PRTE pRte = NULL;
PBYTE ZoneName;
USHORT NetNum;
BYTE CmdType, NwCnt, NumZonesOnNet, ZoneLen;
BOOLEAN RteLocked = FALSE;
BOOLEAN ExtReply = FALSE;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleReply: Enetered\n"));
// For a zip extended reply, the network count is really not
// the # of networks contained in the packet. It is the total #
// of zones on the single network that is described by the reply
NwCnt = NumZonesOnNet = pPkt[ZIP_NW_CNT_OFF];
CmdType = pPkt[ZIP_CMD_OFF];
do
{
// Walk through the reply packet (assuming we asked for the
// contained information). We're still using NwCnt when
// processing an extended reply, but that's okay 'cause it
// will certainly be at least the # of zones contained in
// this packet. The '+3' guarantees that we really have
// network # and node
for (index = ZIP_FIRST_NET_OFF, TotalNetCnt = 0;
(TotalNetCnt < NwCnt) && ((index + 3 ) <= PktLen);
TotalNetCnt ++)
{
if (pRte != NULL)
{
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
pRte = NULL;
}
// Get the next netwotk #, if it's not in our routing
// table (or not the start of a range), then we certainly
// don't care about its zone name
GETSHORT2SHORT(&NetNum, pPkt+index);
index += sizeof(USHORT);
ZoneLen = pPkt[index++];
if (((pRte = AtalkRtmpReferenceRte(NetNum)) == NULL) ||
(pRte->rte_NwRange.anr_FirstNetwork != NetNum))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleReply: Don't know about this range %d\n",
NetNum));
index += ZoneLen;
continue;
}
// Validate the zone name
if ((ZoneLen == 0) || (ZoneLen > MAX_ZONE_LENGTH) ||
((index + ZoneLen) > PktLen))
{
AtalkLogBadPacket(pDdpAddr->ddpao_Node->an_Port,
pSrcAddr,
NULL,
pPkt,
PktLen);
break;
}
// Conditionally move the zone name into the routing table
ZoneName = pPkt+index;
index += ZoneLen;
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = TRUE;
if (AtalkZoneNameOnList(ZoneName, ZoneLen, pRte->rte_ZoneList))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleReply: Already have this zone\n"));
continue;
}
// Check for somebody out there trying to add another zone to
// our directly connected non-extended network and we already
// know its zone.
if ((pRte->rte_NumHops == 0) &&
!EXT_NET(pRte->rte_PortDesc) &&
(AtalkZoneNumOnList(pRte->rte_ZoneList) == 1))
{
AtalkLogBadPacket(pDdpAddr->ddpao_Node->an_Port,
pSrcAddr,
NULL,
pPkt,
PktLen);
continue;
}
// Add to the list now
pRte->rte_ZoneList = AtalkZoneAddToList(pRte->rte_ZoneList,
ZoneName,
ZoneLen);
if (pRte->rte_ZoneList == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleReply: Failed to add zone to list\n"));
pRte->rte_Flags &= ~RTE_ZONELIST_VALID;
continue;
}
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleReply: # of zones known so far %d\n",
AtalkZoneNumOnList(pRte->rte_ZoneList)));
// If not an extended reply, we know that we have all
// of the information about a given network contained
// in this packet, so we can go ahead and mark the zone
// list valid now
if (!ExtReply)
pRte->rte_Flags |= RTE_ZONELIST_VALID;
}
// If we just handled an extended reply, do we now know all
// that we should know about the specified network ?
if (pRte != NULL)
{
if (ExtReply)
{
if (!RteLocked)
{
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = TRUE;
}
if (AtalkZoneNumOnList(pRte->rte_ZoneList) >= NumZonesOnNet)
pRte->rte_Flags |= RTE_ZONELIST_VALID;
}
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
// RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
// pRte = NULL;
}
} while (FALSE);
}
/*** atalkZipHandleQuery
*
*/
VOID
atalkZipHandleQuery(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PATALK_ADDR pSrcAddr,
IN PBYTE pPkt,
IN USHORT PktLen
)
{
PRTE pRte = NULL;
PBUFFER_DESC pBuffDesc,
pBuffDescStart = NULL,
*ppBuffDesc = &pBuffDescStart;
PZONE_LIST pZoneList;
PBYTE Datagram;
ATALK_ERROR error;
ULONG i, CurrNumZones, PrevNumZones, TotalNetCnt;
ULONG NwCnt, NetCntInPkt;
USHORT NetNum, Size;
BOOLEAN AllocNewBuffDesc = TRUE, NewPkt = TRUE;
BOOLEAN PortLocked = FALSE, RteLocked = FALSE;
BYTE CurrReply, NextReply;
SEND_COMPL_INFO SendInfo;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
CurrNumZones = 0;
do
{
// Walk through the query packet building reply packets that
// have as much information as we know.
// When sending replies, we will always send a reply about a
// network that has more than one zone as an extended reply.
// As were walking the query list, and we encounter a couple of
// networks that have only one zone, we'll pack as many of
// these as we can into a non-extended reply
NwCnt = pPkt[ZIP_NW_CNT_OFF];
for (NetCntInPkt = 0, TotalNetCnt = 0, i = ZIP_FIRST_NET_OFF;
(TotalNetCnt < NwCnt) && ((i + sizeof(SHORT)) <= PktLen);
i += sizeof(USHORT), TotalNetCnt++)
{
// Dereference any previous Rtes
if (pRte != NULL)
{
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
pRte = NULL;
}
if (PortLocked)
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
PortLocked = FALSE;
}
// Grab the next network number from the query packet,
// if we don't know about the network, or we don't know
// the zone name, continue with the next network number
GETSHORT2SHORT(&NetNum, pPkt+i);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if ((WITHIN_NETWORK_RANGE(NetNum,&pPortDesc->pd_NetworkRange)) &&
(pPortDesc->pd_ZoneList != NULL))
{
pZoneList = pPortDesc->pd_ZoneList;
PortLocked = TRUE;
}
else
{
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (((pRte = AtalkRtmpReferenceRte(NetNum)) == NULL) ||
(!WITHIN_NETWORK_RANGE(NetNum, &pRte->rte_NwRange)) ||
!(pRte->rte_Flags & RTE_ZONELIST_VALID))
{
continue;
}
else
{
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
pZoneList = pRte->rte_ZoneList;
RteLocked = TRUE;
}
}
next_reply:
if (AllocNewBuffDesc)
{
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,
MAX_DGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("\natalkZipHandleQuery: AtalkAllocBuffDesc @1 failed\n"));
break;
}
Size = 0;
Datagram = pBuffDesc->bd_CharBuffer;
*ppBuffDesc = pBuffDesc;
pBuffDesc->bd_Next = NULL;
ppBuffDesc = &pBuffDesc->bd_Next;
AllocNewBuffDesc = FALSE;
}
// What type of response does this network want ?
// Copy the previous network's zone count. In case of the first
// pass, make it same.
PrevNumZones = CurrNumZones;
CurrNumZones = AtalkZoneNumOnList(pZoneList);
if (i == ZIP_FIRST_NET_OFF)
PrevNumZones = CurrNumZones;
ASSERT (CurrNumZones != 0);
NextReply = ZIP_REPLY;
if (CurrNumZones > 1)
{
// We start a new packet for each extended network
NewPkt = TRUE;
NextReply = ZIP_EXT_REPLY;
if (NetCntInPkt > 0)
{
Datagram[ZIP_CMD_OFF] = CurrReply;
if (CurrReply == ZIP_REPLY)
Datagram[ZIP_NW_CNT_OFF] = (BYTE)NetCntInPkt;
else Datagram[ZIP_NW_CNT_OFF] = (BYTE)PrevNumZones;
AllocNewBuffDesc = TRUE;
pBuffDesc->bd_Length = Size;
NetCntInPkt = 0;
goto next_reply;
}
}
// Walk the zone list
for (; pZoneList != NULL; pZoneList = pZoneList->zl_Next)
{
PZONE pZone = pZoneList->zl_pZone;
// If we're starting to build a new reply packet due to
// either:
//
// 1. first time through
// 2. packet full
// 3. switching reply types
//
// set the index to the first tuple position.
if (NewPkt || (CurrReply != NextReply))
{
if (NetCntInPkt > 0)
{
// Close the current buffdesc and open a new one
// Careful here with the CurrNumZones vs. PrevNumZones
// If we are going from ExtReply to a Reply, we need
// to get PrevNumZones. If we are continuing the
// same ExtReply then we need CurrNumZones.
Datagram[ZIP_CMD_OFF] = CurrReply;
if (CurrReply == ZIP_REPLY)
Datagram[ZIP_NW_CNT_OFF] = (BYTE)NetCntInPkt;
else
{
Datagram[ZIP_NW_CNT_OFF] = (BYTE)CurrNumZones;
if (CurrReply != NextReply)
Datagram[ZIP_NW_CNT_OFF] = (BYTE)PrevNumZones;
}
pBuffDesc->bd_Length = Size;
if ((pBuffDesc = AtalkAllocBuffDesc(NULL,MAX_DGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("\natalkZipHandleQuery: AtalkAllocBuffDesc @2 failed\n"));
break;
}
Size = 0;
Datagram = pBuffDesc->bd_CharBuffer;
*ppBuffDesc = pBuffDesc;
pBuffDesc->bd_Next = NULL;
ppBuffDesc = &pBuffDesc->bd_Next;
NetCntInPkt = 0;
}
Size = ZIP_FIRST_NET_OFF;
CurrReply = NextReply;
NewPkt = FALSE;
}
// We know the answer to the question. Pack a new
// network/zone tuple into the reply packet.
PUTSHORT2SHORT(Datagram+Size, NetNum);
Size += sizeof(USHORT);
Datagram[Size++] = pZone->zn_ZoneLen;
RtlCopyMemory(Datagram + Size,
pZone->zn_Zone,
pZone->zn_ZoneLen);
Size += pZone->zn_ZoneLen;
NetCntInPkt ++;
// If we can't hold another big tuple, signal that we
// should send on the next pass.
if ((Size + sizeof(USHORT) + sizeof(char) + MAX_ZONE_LENGTH)
>= MAX_DGRAM_SIZE)
{
NewPkt = TRUE;
}
}
if (pBuffDesc == NULL)
{
break;
}
}
// Dereference an rte if we broke out the loop above
if (pRte != NULL)
{
ASSERT(!PortLocked);
if (RteLocked)
{
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
// RteLocked = FALSE;
}
AtalkRtmpDereferenceRte(pRte, FALSE);
// pRte = NULL;
}
if (PortLocked)
{
ASSERT(!RteLocked);
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
PortLocked = FALSE;
}
// Close the current buffdesc
if ((!AllocNewBuffDesc) && (pBuffDesc != NULL))
{
pBuffDesc->bd_Length = Size;
if (NetCntInPkt > 0)
{
Datagram[ZIP_CMD_OFF] = CurrReply;
if (CurrReply == ZIP_REPLY)
Datagram[ZIP_NW_CNT_OFF] = (BYTE)NetCntInPkt;
else Datagram[ZIP_NW_CNT_OFF] = (BYTE)CurrNumZones;
}
}
// We have a bunch of datagrams ready to be fired off.
// Make it so. Do not send any with zero lengths, however.
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
for (pBuffDesc = pBuffDescStart;
pBuffDesc != NULL;
pBuffDesc = pBuffDescStart)
{
pBuffDescStart = pBuffDesc->bd_Next;
if (pBuffDesc->bd_Length == 0)
{
ASSERT(pBuffDescStart == NULL);
AtalkFreeBuffDesc(pBuffDesc);
break;
}
// Set the next ptr to be null. Length already set correctly.
pBuffDesc->bd_Next = NULL;
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleQuery: Sending Reply to %d.%d.%d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket));
SendInfo.sc_Ctx1 = pBuffDesc;
error = AtalkDdpSend(pDdpAddr,
pSrcAddr,
DDPPROTO_ZIP,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(error))
{
AtalkFreeBuffDesc(pBuffDesc);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleQuery: AtalkDdpSend %ld\n", error));
}
}
} while (FALSE);
if (PortLocked)
{
ASSERT(!RteLocked);
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
}
}
/*** atalkZipHandleAtpRequest
*
*/
VOID
atalkZipHandleAtpRequest(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PATALK_ADDR pSrcAddr,
IN BYTE CmdType,
IN USHORT TrId,
IN USHORT StartIndex
)
{
PBUFFER_DESC pBuffDesc;
PBYTE Datagram, ZoneName;
PZONE pZone;
ATALK_ERROR error;
int i, ZoneLen, ZoneCnt, CurrZoneIndex, index;
BYTE LastFlag = 0;
SEND_COMPL_INFO SendInfo;
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
do
{
// Allocate a buffer descriptor and initialize the header
if ((pBuffDesc = AtalkAllocBuffDesc(NULL, MAX_DGRAM_SIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
break;
}
Datagram = pBuffDesc->bd_CharBuffer;
Datagram[ATP_CMD_CONTROL_OFF] = ATP_RESPONSE + ATP_EOM_MASK;
Datagram[ATP_SEQ_NUM_OFF] = 0;
PUTSHORT2SHORT(Datagram + ATP_TRANS_ID_OFF, TrId);
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
SendInfo.sc_Ctx1 = pBuffDesc;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
if (CmdType == ZIP_GET_MY_ZONE)
{
// We really shouldn't be getting this request on an
// extended network, but go ahead and reply with the
// "default zone" in this case, of course, reply with
// "desired zone" for non-extended nets. We are a router,
// so "desired zone" will always be valid -- as will the
// default zone for extended net.
PUTSHORT2SHORT(Datagram+ATP_ZIP_LAST_FLAG_OFF, 0);
PUTSHORT2SHORT(Datagram+ATP_ZIP_START_INDEX_OFF, 1);
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
if (EXT_NET(pPortDesc))
{
ZoneName = pPortDesc->pd_DefaultZone->zn_Zone;
ZoneLen = pPortDesc->pd_DefaultZone->zn_ZoneLen;
}
else
{
ZoneName = pPortDesc->pd_DesiredZone->zn_Zone;
ZoneLen = pPortDesc->pd_DesiredZone->zn_ZoneLen;
}
RtlCopyMemory(Datagram+ATP_DATA_OFF+1, ZoneName, ZoneLen);
Datagram[ATP_DATA_OFF] = (BYTE)ZoneLen;
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, (USHORT)(ATP_DATA_OFF + 1 + ZoneLen));
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleAtpReq: Sending GetMyZone Reply to %d.%d.%d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket));
error = AtalkDdpSend(pDdpAddr,
pSrcAddr,
DDPPROTO_ATP,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleAtpRequest: AtalkDdpSend %ld\n", error));
}
break;
}
// Either a GetLocalZones or a GetZoneList. Fill the reply packet
// with as many zones as it'll hold starting at the requested
// start index
index = ATP_DATA_OFF;
if (CmdType == ZIP_GET_LOCAL_ZONES)
{
PZONE_LIST pZoneList;
// For GetLocalZones, we only want to count zones
// that are on the network that is directly connected
// to the port on which the request originated. Use the
// zone list on the port.
ACQUIRE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
for (pZoneList = pPortDesc->pd_ZoneList, ZoneCnt = 0, CurrZoneIndex = 0;
pZoneList != NULL;
pZoneList = pZoneList->zl_Next)
{
// If we have not seen StartIndex zones yet, keep going
if (++CurrZoneIndex < StartIndex)
continue;
pZone = pZoneList->zl_pZone;
// If this packet cannot hold more, we're done (for now)
// Fill in the zone count and the last flag
if ((index + pZone->zn_ZoneLen + 1) >= MAX_DGRAM_SIZE)
{
break;
}
// Place zone name in the packet
ASSERT(pZone != NULL);
Datagram[index] = pZone->zn_ZoneLen;
RtlCopyMemory(Datagram+index+1,
pZone->zn_Zone,
pZone->zn_ZoneLen);
index += (pZone->zn_ZoneLen + 1);
ZoneCnt ++;
}
RELEASE_SPIN_LOCK_DPC(&pPortDesc->pd_Lock);
// We've build a packet, set the last flag, if applicable
LastFlag = (pZoneList == NULL) ? 1 : 0;
}
else // This is a ZIP_GET_ZONE_LIST
{
BOOLEAN PktFull = FALSE;
ASSERT (CmdType == ZIP_GET_ZONE_LIST);
// For GetZoneList, we want all the zones that we know
// of, so use the AtalkZoneTable.
ACQUIRE_SPIN_LOCK_DPC(&AtalkZoneLock);
for (i = 0, ZoneCnt = 0, CurrZoneIndex = 0;
(i < NUM_ZONES_HASH_BUCKETS) && !PktFull; i++)
{
for (pZone = AtalkZonesTable[i];
pZone != NULL;
pZone = pZone->zn_Next)
{
// If we have not seen StartIndex zones yet, keep going
if (++CurrZoneIndex < StartIndex)
continue;
// If this packet cannot hold more, we're done (for now)
// Fill in the zone count and the last flag
if ((index + pZone->zn_ZoneLen + 1) >= MAX_DGRAM_SIZE)
{
PktFull = TRUE;
break;
}
// Place zone name in the packet
Datagram[index] = pZone->zn_ZoneLen;
RtlCopyMemory(Datagram+index+1,
pZone->zn_Zone,
pZone->zn_ZoneLen);
index += (pZone->zn_ZoneLen + 1);
ZoneCnt ++;
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkZoneLock);
// We've build a packet, set the last flag, if applicable
LastFlag = ((i == NUM_ZONES_HASH_BUCKETS) && (pZone == NULL)) ? 1 : 0;
}
// We've build a packet, set the last flag and # of zones in packet
Datagram[ATP_ZIP_LAST_FLAG_OFF] = LastFlag;
Datagram[ATP_ZIP_LAST_FLAG_OFF + 1] = 0;
PUTSHORT2SHORT(Datagram + ATP_ZIP_ZONE_CNT_OFF, ZoneCnt);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, (USHORT)index);
// Finally, send this out
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipHandleAtpReq: Sending LocalZones Reply to %d.%d.%d\n",
pSrcAddr->ata_Network, pSrcAddr->ata_Node, pSrcAddr->ata_Socket));
error = AtalkDdpSend(pDdpAddr,
pSrcAddr,
DDPPROTO_ATP,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(error))
{
AtalkFreeBuffDesc(pBuffDesc);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipHandleAtpRequest: AtalkDdpSend %ld\n", error));
}
} while (FALSE);
}
/*** AtalkZipMulticastAddrForZone
*
*/
VOID
AtalkZipMulticastAddrForZone(
IN PPORT_DESCRIPTOR pPortDesc,
IN PBYTE pZone,
IN BYTE ZoneLen,
IN PBYTE MulticastAddr
)
{
USHORT CheckSum;
BYTE UpCasedZone[MAX_ZONE_LENGTH];
AtalkUpCase(pZone, ZoneLen, UpCasedZone);
// Caculate the checksum for the zone
CheckSum = AtalkDdpCheckSumBuffer(UpCasedZone, ZoneLen, 0);
switch (pPortDesc->pd_PortType)
{
case ELAP_PORT:
case FDDI_PORT:
RtlCopyMemory(MulticastAddr,
AtalkEthernetZoneMulticastAddrsHdr,
ELAP_MCAST_HDR_LEN);
MulticastAddr[ELAP_MCAST_HDR_LEN] =
AtalkEthernetZoneMulticastAddrs[CheckSum % ELAP_ZONE_MULTICAST_ADDRS];
break;
case TLAP_PORT:
RtlCopyMemory(MulticastAddr,
AtalkTokenRingZoneMulticastAddrsHdr,
TLAP_MCAST_HDR_LEN);
RtlCopyMemory(&MulticastAddr[TLAP_MCAST_HDR_LEN],
AtalkTokenRingZoneMulticastAddrs[CheckSum % TLAP_ZONE_MULTICAST_ADDRS],
TLAP_ADDR_LEN - TLAP_MCAST_HDR_LEN);
break;
default:
DBGBRK(DBG_LEVEL_FATAL);
KeBugCheck(0);
}
}
/*** AtalkZipGetNetworkInfoForNode
*
*/
BOOLEAN
AtalkZipGetNetworkInfoForNode(
IN PPORT_DESCRIPTOR pPortDesc,
IN PATALK_NODEADDR pNode,
IN BOOLEAN FindDefZone
)
{
PBUFFER_DESC pBuffDesc = NULL;
ATALK_ADDR SrcAddr, DstAddr;
ATALK_ERROR error;
USHORT NumReqs, DgLen;
BYTE DgCopy[ZIP_ZONENAME_OFF + MAX_ZONE_LENGTH];
KIRQL OldIrql;
BOOLEAN RetCode, Done;
SEND_COMPL_INFO SendInfo;
ASSERT(EXT_NET(pPortDesc));
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (FindDefZone)
{
pPortDesc->pd_Flags &= ~PD_VALID_DEFAULT_ZONE;
pPortDesc->pd_Flags |= PD_FINDING_DEFAULT_ZONE;
}
else
{
pPortDesc->pd_Flags &= ~PD_VALID_DESIRED_ZONE;
pPortDesc->pd_Flags |= PD_FINDING_DESIRED_ZONE;
}
// Get source and destination addresses
SrcAddr.ata_Network = pNode->atn_Network;
SrcAddr.ata_Node = pNode->atn_Node;
SrcAddr.ata_Socket = ZONESINFORMATION_SOCKET;
DstAddr.ata_Network = CABLEWIDE_BROADCAST_NETWORK;
DstAddr.ata_Node = ATALK_BROADCAST_NODE;
DstAddr.ata_Socket = ZONESINFORMATION_SOCKET;
// Build a ZipNetGetInfo datagram
DgCopy[ZIP_CMD_OFF] = ZIP_GET_NETINFO;
DgCopy[ZIP_FLAGS_OFF] = 0;
PUTSHORT2SHORT(DgCopy + ZIP_CABLE_RANGE_START_OFF, 0);
PUTSHORT2SHORT(DgCopy + ZIP_CABLE_RANGE_END_OFF, 0);
DgLen = ZIP_ZONENAME_OFF;
DgCopy[ZIP_ZONELEN_OFF] = 0;
if (!FindDefZone &&
(pPortDesc->pd_InitialDesiredZone != NULL))
{
DgCopy[ZIP_ZONELEN_OFF] = pPortDesc->pd_InitialDesiredZone->zn_ZoneLen;
RtlCopyMemory(DgCopy + ZIP_ZONENAME_OFF,
pPortDesc->pd_InitialDesiredZone->zn_Zone,
pPortDesc->pd_InitialDesiredZone->zn_ZoneLen);
DgLen += DgCopy[ZIP_ZONELEN_OFF];
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
for (NumReqs = 0;
NumReqs < ZIP_NUM_GETNET_INFOS;
NumReqs++)
{
Done = FindDefZone ?
((pPortDesc->pd_Flags & PD_VALID_DEFAULT_ZONE) != 0) :
((pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE) != 0);
if (Done)
{
break;
}
if ((pBuffDesc = AtalkAllocBuffDesc(NULL, DgLen,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
break;
}
RtlCopyMemory(pBuffDesc->bd_CharBuffer, DgCopy, DgLen);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, DgLen);
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
SendInfo.sc_Ctx1 = pBuffDesc;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
error = AtalkDdpTransmit(pPortDesc,
&SrcAddr,
&DstAddr,
DDPPROTO_ZIP,
pBuffDesc,
NULL,
0,
0,
NULL,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(error))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("AtalkZipGetNetworkInfoForNode: AtalkDdpTransmit %ld\n", error));
break;
}
pBuffDesc = NULL;
AtalkSleep(ZIP_GET_NETINFO_WAIT);
}
if (pBuffDesc != NULL)
AtalkFreeBuffDesc(pBuffDesc);
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (FindDefZone)
{
pPortDesc->pd_Flags &= ~PD_FINDING_DEFAULT_ZONE;
}
else
{
pPortDesc->pd_Flags &= ~PD_FINDING_DESIRED_ZONE;
}
RetCode = FindDefZone ?
((pPortDesc->pd_Flags & PD_VALID_DEFAULT_ZONE) == PD_VALID_DEFAULT_ZONE) :
((pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE) == PD_VALID_DESIRED_ZONE);
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
return RetCode;
}
/*** AtalkZipGetMyZone
*
*/
ATALK_ERROR
AtalkZipGetMyZone(
IN PPORT_DESCRIPTOR pPortDesc,
IN BOOLEAN fDesired,
IN OUT PAMDL pAMdl,
IN INT Size,
IN PACTREQ pActReq
)
{
PZIPCOMPLETIONINFO pZci = NULL;
ATALK_ERROR Status = ATALK_NO_ERROR;
ULONG BytesCopied;
PZONE pZone;
KIRQL OldIrql;
BOOLEAN Done = FALSE;
ASSERT (VALID_ACTREQ(pActReq));
if (Size < (MAX_ZONE_LENGTH + 1))
return ATALK_BUFFER_TOO_SMALL;
do
{
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
// For extended network, we either know or cannot find out
if (EXT_NET(pPortDesc))
{
BOOLEAN Yes = FALSE;
if (fDesired && (pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE))
{
pZone = pPortDesc->pd_DesiredZone;
Yes = TRUE;
}
else if (!fDesired && (pPortDesc->pd_Flags & PD_VALID_DEFAULT_ZONE))
{
pZone = pPortDesc->pd_DefaultZone;
Yes = TRUE;
}
if (Yes)
{
TdiCopyBufferToMdl( pZone->zn_Zone,
0,
pZone->zn_ZoneLen,
pAMdl,
0,
&BytesCopied);
ASSERT (BytesCopied == pZone->zn_ZoneLen);
TdiCopyBufferToMdl( "",
0,
1,
pAMdl,
pZone->zn_ZoneLen,
&BytesCopied);
ASSERT (BytesCopied == 1);
Done = TRUE;
}
}
// For non-extended networks, we need to ask a router. If we don't
// know about a router, return.
if (!Done &&
(EXT_NET(pPortDesc) ||
!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY)))
{
TdiCopyBufferToMdl( "*",
0,
sizeof("*"),
pAMdl,
0,
&BytesCopied);
ASSERT (BytesCopied == sizeof("*"));
Done = TRUE;
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
if (Done)
{
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
break;
}
ASSERT (!EXT_NET(pPortDesc));
// Allocate a Completion info structure
if ((pZci = AtalkAllocMemory(sizeof(ZIPCOMPLETIONINFO))) == NULL)
{
Status = ATALK_RESR_MEM;
break;
}
// Initialize completion info
#if DBG
pZci->zci_Signature = ZCI_SIGNATURE;
#endif
INITIALIZE_SPIN_LOCK(&pZci->zci_Lock);
pZci->zci_RefCount = 1;
pZci->zci_pPortDesc = pPortDesc;
pZci->zci_pDdpAddr = NULL;
pZci->zci_pAMdl = pAMdl;
pZci->zci_BufLen = Size;
pZci->zci_pActReq = pActReq;
pZci->zci_Router.ata_Network = pPortDesc->pd_ARouter.atn_Network;
pZci->zci_Router.ata_Node = pPortDesc->pd_ARouter.atn_Node;
pZci->zci_Router.ata_Socket = ZONESINFORMATION_SOCKET;
pZci->zci_ExpirationCount = ZIP_GET_ZONEINFO_RETRIES;
pZci->zci_NextZoneOff = 0;
pZci->zci_ZoneCount = -1;
pZci->zci_AtpRequestType = ZIP_GET_MY_ZONE;
pZci->zci_Handler = atalkZipGetMyZoneReply;
AtalkTimerInitialize(&pZci->zci_Timer,
atalkZipZoneInfoTimer,
ZIP_GET_ZONEINFO_TIMER);
Status = atalkZipSendPacket(pZci, TRUE);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipGetMyZone: atalkZipSendPacket %ld\n",
Status));
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
Status = ATALK_PENDING; // atalkZipDereferenceZci completes the req
}
} while (FALSE);
return(Status);
}
/*** atalkZipGetMyZoneReply
*
*/
VOID
atalkZipGetMyZoneReply(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR Status,
IN BYTE DdpType,
IN PZIPCOMPLETIONINFO pZci,
IN BOOLEAN OptimizePath,
IN PVOID OptimizeCtx
)
{
ULONG BytesCopied;
KIRQL OldIrql;
USHORT ZoneCnt;
BYTE ZoneLen;
do
{
if (Status == ATALK_SOCKET_CLOSED)
{
pZci->zci_pDdpAddr = NULL;
if (AtalkTimerCancelEvent(&pZci->zci_Timer, NULL))
atalkZipDereferenceZci(pZci);
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
break;
}
if ((Status != ATALK_NO_ERROR) ||
(DdpType != DDPPROTO_ATP) ||
(PktLen <= ATP_ZIP_FIRST_ZONE_OFF))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
// We should have one zone
GETSHORT2SHORT(&ZoneCnt, pPkt + ATP_ZIP_ZONE_CNT_OFF);
ZoneLen = pPkt[ATP_ZIP_FIRST_ZONE_OFF];
if ((ZoneCnt != 1) ||
(ZoneLen == 0) || (ZoneLen > MAX_ZONE_LENGTH))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetMyZoneReply: Bad reply\n"));
break;
}
if (AtalkTimerCancelEvent(&pZci->zci_Timer, NULL))
{
atalkZipDereferenceZci(pZci);
}
ACQUIRE_SPIN_LOCK(&pZci->zci_Lock, &OldIrql);
TdiCopyBufferToMdl( pPkt + ATP_ZIP_FIRST_ZONE_OFF + 1,
0,
ZoneLen,
pZci->zci_pAMdl,
0,
&BytesCopied);
ASSERT (BytesCopied == ZoneLen);
TdiCopyBufferToMdl( "",
0,
1,
pZci->zci_pAMdl,
ZoneLen,
&BytesCopied);
ASSERT (BytesCopied == 1);
pZci->zci_FinalStatus = ATALK_NO_ERROR;
RELEASE_SPIN_LOCK(&pZci->zci_Lock, OldIrql);
atalkZipDereferenceZci(pZci);
} while (FALSE);
}
/*** AtalkZipGetZoneList
*
*/
ATALK_ERROR
AtalkZipGetZoneList(
IN PPORT_DESCRIPTOR pPortDesc,
IN BOOLEAN fLocalZones,
IN OUT PAMDL pAMdl,
IN INT Size,
IN PACTREQ pActReq
)
{
PZIPCOMPLETIONINFO pZci = NULL;
ATALK_ERROR Status = ATALK_NO_ERROR;
ULONG BytesCopied, index, NumZones;
KIRQL OldIrql;
BOOLEAN Done = FALSE, PortLocked = TRUE;
ASSERT (VALID_ACTREQ(pActReq));
if (Size < (MAX_ZONE_LENGTH + 1))
return ATALK_BUFFER_TOO_SMALL;
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
do
{
// If we don't know about a router, return.
if (!(pPortDesc->pd_Flags & PD_SEEN_ROUTER_RECENTLY))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_WARN,
("AtalkZipGetZoneList: Don't know a router !!!\n"));
TdiCopyBufferToMdl( "*",
0,
sizeof("*"),
pAMdl,
0,
&BytesCopied);
ASSERT (BytesCopied == sizeof("*"));
Done = TRUE;
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
PortLocked = FALSE;
if (Done)
{
((PZIP_GETZONELIST_PARAMS)(pActReq->ar_pParms))->ZonesAvailable = 1;
(*pActReq->ar_Completion)(ATALK_NO_ERROR, pActReq);
break;
}
// If we are a router, then simply copy the zones. Else send a
// a request to the router. DO NOT SEND A REQUEST IF WE ARE A
// ROUTER SINCE THAT WILL RESULT IN A HORRIBLE RECURSION RESULTING
// IN A DOUBLE FAULT (OUT OF STACK SPACE).
if (pPortDesc->pd_Flags & PD_ROUTER_RUNNING)
{
PZONE pZone;
NumZones = 0;
if (fLocalZones)
{
PZONE_LIST pZoneList;
// For GetLocalZones, we only want to count zones
// that are on the network that is directly connected
// to the port on which the request originated
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
for (index = 0, pZoneList = pPortDesc->pd_ZoneList;
pZoneList != NULL;
pZoneList = pZoneList->zl_Next)
{
pZone = pZoneList->zl_pZone;
ASSERT (pZone != NULL);
// If this packet cannot hold more, we're done
if ((INT)(index + pZone->zn_ZoneLen + 1) >= Size)
{
break;
}
// Place zone name in the packet
TdiCopyBufferToMdl( pZone->zn_Zone,
0,
pZone->zn_ZoneLen + 1,
pAMdl,
index,
&BytesCopied);
ASSERT (BytesCopied == (ULONG)(pZone->zn_ZoneLen + 1));
NumZones ++;
index += (pZone->zn_ZoneLen + 1);
}
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
Status = (pZoneList != NULL) ?
ATALK_BUFFER_TOO_SMALL : ATALK_NO_ERROR;
}
else
{
BOOLEAN PktFull = FALSE;
INT i;
ACQUIRE_SPIN_LOCK(&AtalkZoneLock, &OldIrql);
for (i = 0, index = 0, PktFull = FALSE;
(i < NUM_ZONES_HASH_BUCKETS) && !PktFull;
i++)
{
for (pZone = AtalkZonesTable[i];
pZone != NULL;
pZone = pZone->zn_Next)
{
// If this packet cannot hold more, we're done
if ((INT)(index + pZone->zn_ZoneLen + 1) >= Size)
{
PktFull = TRUE;
break;
}
// Place zone name in the packet
TdiCopyBufferToMdl( pZone->zn_Zone,
0,
pZone->zn_ZoneLen + 1,
pAMdl,
index,
&BytesCopied);
ASSERT (BytesCopied == (ULONG)(pZone->zn_ZoneLen + 1));
NumZones ++;
index += (pZone->zn_ZoneLen + 1);
}
}
RELEASE_SPIN_LOCK(&AtalkZoneLock, OldIrql);
Status = ((pZone != NULL) || ( i < NUM_ZONES_HASH_BUCKETS)) ?
ATALK_BUFFER_TOO_SMALL : ATALK_NO_ERROR;
}
((PZIP_GETZONELIST_PARAMS)
(pActReq->ar_pParms))->ZonesAvailable = NumZones;
if (ATALK_SUCCESS(Status))
{
(*pActReq->ar_Completion)(Status, pActReq);
}
break;
}
ASSERT ((pPortDesc->pd_Flags & PD_ROUTER_RUNNING) == 0);
// Allocate a Completion info structure
if ((pZci = AtalkAllocMemory(sizeof(ZIPCOMPLETIONINFO))) == NULL)
{
Status = ATALK_RESR_MEM;
break;
}
// Initialize completion info
#if DBG
pZci->zci_Signature = ZCI_SIGNATURE;
#endif
INITIALIZE_SPIN_LOCK(&pZci->zci_Lock);
pZci->zci_RefCount = 1;
pZci->zci_pPortDesc = pPortDesc;
pZci->zci_pDdpAddr = NULL;
pZci->zci_pAMdl = pAMdl;
pZci->zci_BufLen = Size;
pZci->zci_pActReq = pActReq;
pZci->zci_Router.ata_Network = pPortDesc->pd_ARouter.atn_Network;
pZci->zci_Router.ata_Node = pPortDesc->pd_ARouter.atn_Node;
pZci->zci_Router.ata_Socket = ZONESINFORMATION_SOCKET;
pZci->zci_ExpirationCount = ZIP_GET_ZONEINFO_RETRIES;
pZci->zci_NextZoneOff = 0;
pZci->zci_ZoneCount = 0;
pZci->zci_AtpRequestType = ZIP_GET_ZONE_LIST;
AtalkTimerInitialize(&pZci->zci_Timer,
atalkZipZoneInfoTimer,
ZIP_GET_ZONEINFO_TIMER);
if (fLocalZones)
pZci->zci_AtpRequestType = ZIP_GET_LOCAL_ZONES;
pZci->zci_Handler = atalkZipGetZoneListReply;
Status = atalkZipSendPacket(pZci, TRUE);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("AtalkZipGetZoneList: atalkZipSendPacket %ld\n", Status));
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
Status = ATALK_PENDING; // atalkZipDereferenceZci completes the req
}
} while (FALSE);
if (PortLocked)
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
return(Status);
}
/*** atalkZipGetZoneListReply
*
*/
VOID
atalkZipGetZoneListReply(
IN PPORT_DESCRIPTOR pPortDesc,
IN PDDP_ADDROBJ pDdpAddr,
IN PBYTE pPkt,
IN USHORT PktLen,
IN PATALK_ADDR pSrcAddr,
IN PATALK_ADDR pDstAddr,
IN ATALK_ERROR Status,
IN BYTE DdpType,
IN PZIPCOMPLETIONINFO pZci,
IN BOOLEAN OptimizePath,
IN PVOID OptimizeCtx
)
{
PBYTE pZone;
ULONG dindex;
ULONG BytesCopied;
USHORT ZoneCnt;
BYTE ZoneLen;
BOOLEAN LastFlag, Overflow = FALSE;
ASSERT(VALID_ZCI(pZci));
do
{
if (Status == ATALK_SOCKET_CLOSED)
{
pZci->zci_pDdpAddr = NULL;
if (AtalkTimerCancelEvent(&pZci->zci_Timer, NULL))
{
atalkZipDereferenceZci(pZci);
}
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
break;
}
if ((Status != ATALK_NO_ERROR) ||
(DdpType != DDPPROTO_ATP) ||
(PktLen <= ATP_ZIP_FIRST_ZONE_OFF))
{
AtalkLogBadPacket(pPortDesc,
pSrcAddr,
pDstAddr,
pPkt,
PktLen);
break;
}
// We should have a zone list.
// Cancel the timer. Start it again if we have not got all the zones
if (AtalkTimerCancelEvent(&pZci->zci_Timer, NULL))
{
atalkZipDereferenceZci(pZci);
}
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListReply: More zones. Index %d, SizeLeft %d\n",
pZci->zci_ZoneCount, pZci->zci_BufLen - pZci->zci_NextZoneOff));
ACQUIRE_SPIN_LOCK_DPC(&pZci->zci_Lock);
GETSHORT2SHORT(&ZoneCnt, pPkt + ATP_ZIP_ZONE_CNT_OFF);
LastFlag = FALSE;
if ((pPkt[ATP_ZIP_LAST_FLAG_OFF] != 0) ||
(ZoneCnt == 0))
LastFlag = TRUE;
dindex = ATP_ZIP_FIRST_ZONE_OFF;
while (ZoneCnt != 0)
{
// Pull out the next zone
ZoneLen = pPkt[dindex++];
if ((ZoneLen == 0) ||
(ZoneLen > MAX_ZONE_LENGTH) ||
(PktLen < (dindex + ZoneLen)))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetZoneListReply: Bad Zip reply\n"));
break;
}
pZone = pPkt + dindex;
dindex += ZoneLen;
if ((pZci->zci_NextZoneOff + ZoneLen + 1) > pZci->zci_BufLen)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipGetZoneList: Overflow\n"));
Overflow = TRUE;
break;
}
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("AtalkZipGetZoneList: Copying a zone (%d, %d)\n",
pZci->zci_ZoneCount,
pZci->zci_BufLen - pZci->zci_NextZoneOff));
TdiCopyBufferToMdl( pZone,
0,
ZoneLen,
pZci->zci_pAMdl,
pZci->zci_NextZoneOff,
&BytesCopied);
ASSERT (BytesCopied == ZoneLen);
TdiCopyBufferToMdl( "",
0,
1,
pZci->zci_pAMdl,
pZci->zci_NextZoneOff + ZoneLen,
&BytesCopied);
pZci->zci_NextZoneOff += (ZoneLen + 1);
pZci->zci_ZoneCount ++;
ZoneCnt --;
}
RELEASE_SPIN_LOCK_DPC(&pZci->zci_Lock);
if (Overflow || LastFlag)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListReply: All that we wanted\n"));
pZci->zci_FinalStatus = ATALK_NO_ERROR;
if (Overflow)
pZci->zci_FinalStatus = ATALK_BUFFER_TOO_SMALL;
((PZIP_GETZONELIST_PARAMS)
(pZci->zci_pActReq->ar_pParms))->ZonesAvailable =
pZci->zci_ZoneCount;
atalkZipDereferenceZci(pZci);
}
else
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListReply: Sending another packet\n"));
Status = atalkZipSendPacket(pZci, TRUE);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("AtalkZipGetZoneListReply: atalkZipSendPacket %ld\n", Status));
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
}
}
} while (FALSE);
}
/*** atalkZipZoneInfoTimer
*
*/
LOCAL LONG FASTCALL
atalkZipZoneInfoTimer(
IN PTIMERLIST pTimer,
IN BOOLEAN TimerShuttingDown
)
{
PZIPCOMPLETIONINFO pZci;
ATALK_ERROR Status;
ULONG BytesCopied;
BOOLEAN Done = FALSE, RestartTimer = FALSE;
pZci = (PZIPCOMPLETIONINFO)CONTAINING_RECORD(pTimer, ZIPCOMPLETIONINFO, zci_Timer);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipZoneInfoTimer: Entered for pZci = %lx\n", pZci));
ASSERT(VALID_ZCI(pZci));
do
{
ACQUIRE_SPIN_LOCK_DPC(&pZci->zci_Lock);
if (--(pZci->zci_ExpirationCount) != 0)
{
RestartTimer = TRUE;
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipZoneInfoTimer: Sending another packet\n", pZci));
RELEASE_SPIN_LOCK_DPC(&pZci->zci_Lock);
Status = atalkZipSendPacket(pZci, FALSE);
if (!ATALK_SUCCESS(Status))
{
RestartTimer = FALSE;
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipZoneInfoTimer: atalkZipSendPacket %ld\n",
Status));
pZci->zci_FinalStatus = Status;
atalkZipDereferenceZci(pZci);
}
break;
}
if (pZci->zci_AtpRequestType == ZIP_GET_MY_ZONE)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipZoneInfoTimer: Completing GetMyZone\n"));
TdiCopyBufferToMdl("*",
0,
sizeof("*"),
pZci->zci_pAMdl,
0,
&BytesCopied);
}
else // GET_ZONE_LIST
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipZoneInfoTimer: Completing GetZoneList\n"));
if ((pZci->zci_ZoneCount == 0) &&
((SHORT)(pZci->zci_NextZoneOff + sizeof("*")) < pZci->zci_BufLen))
{
pZci->zci_ZoneCount++;
TdiCopyBufferToMdl("*",
0,
sizeof("*"),
pZci->zci_pAMdl,
pZci->zci_NextZoneOff,
&BytesCopied);
ASSERT (BytesCopied == sizeof("*"));
}
((PZIP_GETZONELIST_PARAMS)
(pZci->zci_pActReq->ar_pParms))->ZonesAvailable =
pZci->zci_ZoneCount;
}
RELEASE_SPIN_LOCK_DPC(&pZci->zci_Lock);
atalkZipDereferenceZci(pZci); // Timer reference
pZci->zci_FinalStatus = ATALK_NO_ERROR;
atalkZipDereferenceZci(pZci);
ASSERT(!RestartTimer);
} while (FALSE);
return (RestartTimer ? ATALK_TIMER_REQUEUE : ATALK_TIMER_NO_REQUEUE);
}
/*** atalkZipSendPacket
*
*/
ATALK_ERROR
atalkZipSendPacket(
IN PZIPCOMPLETIONINFO pZci,
IN BOOLEAN EnqueueTimer
)
{
PBUFFER_DESC pBuffDesc;
ATALK_ERROR Status;
ATALK_ADDR DestAddr;
PBYTE Datagram;
SEND_COMPL_INFO SendInfo;
ASSERT (VALID_ZCI(pZci));
if (pZci->zci_pDdpAddr == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipSendPacket: Opening Ddp Socket\n"));
// Open a socket for handling replies
Status = AtalkDdpOpenAddress(pZci->zci_pPortDesc,
UNKNOWN_SOCKET,
NULL,
pZci->zci_Handler,
pZci,
0,
NULL,
&pZci->zci_pDdpAddr);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipSendPacket: AtalkDdpOpenAddress %ld\n", Status));
return Status;
}
// mark the fact that this is an "internal" socket
pZci->zci_pDdpAddr->ddpao_Flags |= DDPAO_SOCK_INTERNAL;
}
ASSERT (VALID_DDP_ADDROBJ(pZci->zci_pDdpAddr));
// Alloc a buffer desciptor for the atp request
if ((pBuffDesc = AtalkAllocBuffDesc(pZci->zci_Datagram,
ZIP_GETZONELIST_DDPSIZE,
BD_CHAR_BUFFER)) == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipSendPacket: Couldn't allocate a buffdesc\n"));
AtalkDdpCloseAddress(pZci->zci_pDdpAddr, NULL, NULL);
return ATALK_RESR_MEM;
}
// Start the zone info timer
if (EnqueueTimer)
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&pZci->zci_Lock, &OldIrql);
pZci->zci_RefCount ++; // For the timer
AtalkTimerScheduleEvent(&pZci->zci_Timer);
RELEASE_SPIN_LOCK(&pZci->zci_Lock, OldIrql);
}
// Build the Atp request and send it along
Datagram = pBuffDesc->bd_CharBuffer;
Datagram[ATP_CMD_CONTROL_OFF] = ATP_REQUEST;
Datagram[ATP_BITMAP_OFF] = 1;
Datagram[ATP_TRANS_ID_OFF] = 0;
Datagram[ATP_TRANS_ID_OFF+1] = 0;
Datagram[ATP_ZIP_CMD_OFF] = (BYTE)pZci->zci_AtpRequestType;
Datagram[ATP_ZIP_CMD_OFF+1] = 0;
PUTSHORT2SHORT(Datagram + ATP_ZIP_START_INDEX_OFF, pZci->zci_ZoneCount+1);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, ZIP_GETZONELIST_DDPSIZE);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipSendPacket: Sending the packet along\n"));
DestAddr = pZci->zci_Router;
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
SendInfo.sc_Ctx1 = pBuffDesc;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
if (!ATALK_SUCCESS(Status = AtalkDdpSend(pZci->zci_pDdpAddr,
&DestAddr,
DDPPROTO_ATP,
FALSE,
pBuffDesc,
NULL,
0,
NULL,
&SendInfo)))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipSendPacket: AtalkDdpSend %ld\n", Status));
AtalkFreeBuffDesc(pBuffDesc);
if (AtalkTimerCancelEvent(&pZci->zci_Timer, NULL))
{
atalkZipDereferenceZci(pZci);
}
}
else Status = ATALK_PENDING;
return Status;
}
/*** atalkZipDereferenceZci
*
*/
LOCAL VOID
atalkZipDereferenceZci(
IN PZIPCOMPLETIONINFO pZci
)
{
BOOLEAN Done;
KIRQL OldIrql;
ASSERT(VALID_ZCI(pZci));
ACQUIRE_SPIN_LOCK(&pZci->zci_Lock, &OldIrql);
Done = (--(pZci->zci_RefCount) == 0);
RELEASE_SPIN_LOCK(&pZci->zci_Lock, OldIrql);
if (Done)
{
if (pZci->zci_pDdpAddr != NULL)
AtalkDdpCloseAddress(pZci->zci_pDdpAddr, NULL, NULL);
(*pZci->zci_pActReq->ar_Completion)(pZci->zci_FinalStatus, pZci->zci_pActReq);
// Unlock the Zip stuff back again, if there are no more pending zip operations
AtalkUnlockZipIfNecessary();
AtalkFreeMemory(pZci);
}
}
// We do not want to send too many queries per invocation of the timer. This is to avoid
// spending too much time within the timer Dpc and also using up all of the Ndis packets
// and buffers during this.
#define MAX_QUERIES_PER_INVOCATION 75
// Structure used by the atalkZipQueryTimer routine
typedef struct _QueryTimerData
{
struct _QueryTimerData * qtd_Next;
BOOLEAN qtd_SkipThis;
PBUFFER_DESC qtd_pBuffDesc;
ATALK_ADDR qtd_DstAddr;
PDDP_ADDROBJ qtd_pDdpAddr;
} QTD, *PQTD;
/*** atalkZipQueryTimer
*
* When we are a router and if any of our RTEs do not have a valid zone list, we send
* out queries to other routers who do.
*/
LOCAL LONG FASTCALL
atalkZipQueryTimer(
IN PTIMERLIST pContext,
IN BOOLEAN TimerShuttingDown
)
{
PPORT_DESCRIPTOR pPortDesc;
ATALK_ADDR SrcAddr;
PRTE pRte;
PBYTE Datagram;
PQTD pQtd, pQtdStart = NULL, *ppQtd = &pQtdStart;
ATALK_ERROR Status;
int i, j;
SEND_COMPL_INFO SendInfo;
if (TimerShuttingDown)
return ATALK_TIMER_NO_REQUEUE;
// Go through the routing tables and send out a query to any network
// that we do not know the zone name of
ACQUIRE_SPIN_LOCK_DPC(&AtalkRteLock)
for (i = 0, j = 0;
(i < NUM_RTMP_HASH_BUCKETS) && (j <= MAX_QUERIES_PER_INVOCATION);
i++)
{
for (pRte = AtalkRoutingTable[i]; pRte != NULL; pRte = pRte->rte_Next)
{
if (pRte->rte_Flags & RTE_ZONELIST_VALID)
{
continue;
}
// If login is to restrict access to zones over dial up connection
// put restrictions here
if (pRte->rte_PortDesc == RasPortDesc)
{
continue;
}
// Up the count of # of datagrams used. Do need exceed max.
if (++j >= MAX_QUERIES_PER_INVOCATION)
break;
if (((pQtd = AtalkAllocMemory(sizeof(QTD))) == NULL) ||
((pQtd->qtd_pBuffDesc = AtalkAllocBuffDesc(NULL,
ZIP_ONEZONEQUERY_DDPSIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL))
{
if (pQtd != NULL)
AtalkFreeMemory(pQtd);
break;
}
*ppQtd = pQtd;
pQtd->qtd_Next = NULL;
ppQtd = &pQtd->qtd_Next;
pQtd->qtd_SkipThis = FALSE;
Datagram = pQtd->qtd_pBuffDesc->bd_CharBuffer;
// Build the datagram and send it on its way
Datagram[ZIP_CMD_OFF] = ZIP_QUERY;
Datagram[ZIP_NW_CNT_OFF] = 1;
PUTSHORT2SHORT(Datagram+ZIP_FIRST_NET_OFF,
pRte->rte_NwRange.anr_FirstNetwork);
// Compute the source and destination
SrcAddr.ata_Network = pRte->rte_PortDesc->pd_ARouter.atn_Network;
SrcAddr.ata_Node = pRte->rte_PortDesc->pd_ARouter.atn_Node;
SrcAddr.ata_Socket = ZONESINFORMATION_SOCKET;
pQtd->qtd_DstAddr.ata_Socket = ZONESINFORMATION_SOCKET;
if (pRte->rte_NumHops == 0)
{
pQtd->qtd_DstAddr = SrcAddr;
}
else
{
pQtd->qtd_DstAddr.ata_Network = pRte->rte_NextRouter.atn_Network;
pQtd->qtd_DstAddr.ata_Node = pRte->rte_NextRouter.atn_Node;
}
// Map source address to the ddp address object (open socket)
AtalkDdpReferenceByAddr(pRte->rte_PortDesc,
&SrcAddr,
&pQtd->qtd_pDdpAddr,
&Status);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipQueryTimer: DdpRefByAddr failed for %d.%d\n",
SrcAddr.ata_Network, SrcAddr.ata_Node));
pQtd->qtd_pDdpAddr = NULL;
pQtd->qtd_SkipThis = TRUE;
}
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkRteLock);
// We have a bunch of datagrams ready to be fired off.
// Make it so.
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
for (pQtd = pQtdStart; pQtd != NULL; pQtd = pQtdStart)
{
pQtdStart = pQtd->qtd_Next;
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pQtd->qtd_pBuffDesc, ZIP_ONEZONEQUERY_DDPSIZE);
SendInfo.sc_Ctx1 = pQtd->qtd_pBuffDesc;
if (pQtd->qtd_SkipThis ||
!ATALK_SUCCESS(AtalkDdpSend(pQtd->qtd_pDdpAddr,
&pQtd->qtd_DstAddr,
DDPPROTO_ZIP,
FALSE,
pQtd->qtd_pBuffDesc,
NULL,
0,
NULL,
&SendInfo)))
{
AtalkFreeBuffDesc(pQtd->qtd_pBuffDesc);
}
if (pQtd->qtd_pDdpAddr != NULL)
AtalkDdpDereferenceDpc(pQtd->qtd_pDdpAddr);
AtalkFreeMemory(pQtd);
}
return ATALK_TIMER_REQUEUE;
}
/*** atalkZipGetZoneListForPort
*
*/
LOCAL BOOLEAN
atalkZipGetZoneListForPort(
IN PPORT_DESCRIPTOR pPortDesc
)
{
PRTE pRte;
PBUFFER_DESC pBuffDesc = NULL;
ATALK_ADDR SrcAddr, DstAddr;
ATALK_ERROR Status;
KIRQL OldIrql;
int NumReqs = 0;
BOOLEAN RetCode = FALSE;
BYTE MulticastAddr[ELAP_ADDR_LEN];
SEND_COMPL_INFO SendInfo;
ASSERT (KeGetCurrentIrql() == LOW_LEVEL);
// Similar to RTMP finding out the network number attached to port, our
// task is to find out the zone list of the network attached to a
// particular port. We too don't want to mess up a working AppleTalk
// internet. So, spend a little while doing zone queries to see if the
// network already has a zone list -- if we find one, use it; else, we
// had better be a seed router.
// Set source and destination address
SrcAddr.ata_Node = pPortDesc->pd_ARouter.atn_Node;
SrcAddr.ata_Network = pPortDesc->pd_ARouter.atn_Network;
SrcAddr.ata_Socket = ZONESINFORMATION_SOCKET;
DstAddr.ata_Network = CABLEWIDE_BROADCAST_NETWORK;
DstAddr.ata_Node = ATALK_BROADCAST_NODE;
DstAddr.ata_Socket = ZONESINFORMATION_SOCKET;
if ((pRte = AtalkRtmpReferenceRte(pPortDesc->pd_NetworkRange.anr_FirstNetwork)) == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetZoneListForPort: Could not reference Rte for nwrange on port\n"));
return FALSE;
}
// Blast a few queries and see if anybody knows our zone-name
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
pPortDesc->pd_Flags |= PD_FINDING_DEFAULT_ZONE;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
SendInfo.sc_TransmitCompletion = atalkZipSendComplete;
// SendInfo.sc_Ctx2 = NULL;
// SendInfo.sc_Ctx3 = NULL;
while ((NumReqs < (ZIP_NUM_QUERIES * ZIP_NUM_RETRIES)) &&
!(pRte->rte_Flags & RTE_ZONELIST_VALID))
{
if ((NumReqs % ZIP_NUM_RETRIES) == 0)
{
if ((pBuffDesc = AtalkAllocBuffDesc(NULL, ZIP_ONEZONEQUERY_DDPSIZE,
BD_CHAR_BUFFER | BD_FREE_BUFFER)) == NULL)
{
break;
}
pBuffDesc->bd_CharBuffer[ZIP_CMD_OFF] = ZIP_QUERY;
pBuffDesc->bd_CharBuffer[ZIP_NW_CNT_OFF] = 1;
PUTSHORT2SHORT(pBuffDesc->bd_CharBuffer + ZIP_FIRST_NET_OFF,
pPortDesc->pd_NetworkRange.anr_FirstNetwork);
// Set the length in the buffer descriptor.
AtalkSetSizeOfBuffDescData(pBuffDesc, ZIP_ONEZONEQUERY_DDPSIZE);
SendInfo.sc_Ctx1 = pBuffDesc;
Status = AtalkDdpTransmit(pPortDesc,
&SrcAddr,
&DstAddr,
DDPPROTO_ZIP,
pBuffDesc,
NULL,
0,
0,
NULL,
NULL,
&SendInfo);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetZoneListForPort: AtalkDdpTransmit %ld\n", Status));
break;
}
pBuffDesc = NULL;
}
NumReqs++;
AtalkSleep(ZIP_QUERY_WAIT);
}
// We either got an answer or we did not. In the latter case we should
// be seeding.
do
{
if (pRte->rte_Flags & RTE_ZONELIST_VALID)
{
// We got an answer. The valid zone list is in the routing table
// Move it to the port descriptor
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
ACQUIRE_SPIN_LOCK_DPC(&pRte->rte_Lock);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListForPort: Moving ZoneList from Rte to Port %Z\n",
&pPortDesc->pd_AdapterKey));
pPortDesc->pd_ZoneList = AtalkZoneCopyList(pRte->rte_ZoneList);
RELEASE_SPIN_LOCK_DPC(&pRte->rte_Lock);
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
if (pPortDesc->pd_ZoneList == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListForPort: Failed to Move ZoneList from Rte to Port\n"));
break;
}
// If this is an extended network, we should already have "ThisZone"
// set (due to GetNetInfo's when we allocated this node), if not
// find out the true default zone
if (EXT_NET(pPortDesc))
{
PDDP_ADDROBJ pDdpAddr = NULL;
ATALK_ADDR Addr;
ATALK_ERROR Status;
// The router's Zip packet handler doesn't want to be told
// about zones (it thinks it knows), so it ignores
// NetInfoReplies. Switch back to the non-router Zip handler
// while we do a GetNetworkInfoForNode
Addr.ata_Node = pPortDesc->pd_ARouter.atn_Node;
Addr.ata_Network = pPortDesc->pd_ARouter.atn_Network;
Addr.ata_Socket = ZONESINFORMATION_SOCKET;
AtalkDdpReferenceByAddr(pPortDesc, &Addr, &pDdpAddr, &Status);
if (!ATALK_SUCCESS(Status))
{
DBGPRINT(DBG_COMP_RTMP, DBG_LEVEL_ERR,
("atalkZipGetZoneListForPort: AtalkDdpRefByAddr %ld for %d.%d\n",
Status, Addr.ata_Network, Addr.ata_Node));
break;
}
AtalkDdpNewHandlerForSocket(pDdpAddr,
AtalkZipPacketIn,
pPortDesc);
if ((!(pPortDesc->pd_Flags & PD_VALID_DESIRED_ZONE) &&
!AtalkZipGetNetworkInfoForNode(pPortDesc,
&pPortDesc->pd_ARouter,
FALSE)) ||
!AtalkZipGetNetworkInfoForNode(pPortDesc,
&pPortDesc->pd_ARouter,
TRUE))
{
AtalkDdpDereference(pDdpAddr);
break;
}
// Switch back the handler to the router's version
AtalkDdpNewHandlerForSocket(pDdpAddr,
AtalkZipPacketInRouter,
pPortDesc);
AtalkDdpDereference(pDdpAddr);
// The default zone had better be on the list we just
// received
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (!AtalkZoneOnList(pPortDesc->pd_DefaultZone,
pPortDesc->pd_ZoneList) ||
!AtalkZoneOnList(pPortDesc->pd_DesiredZone,
pPortDesc->pd_ZoneList))
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetZoneListForPort: Ext port, Default/Desired zone not on list\n"));
}
else RetCode = TRUE;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
}
else
{
// On non-extended network, the one entry on the zone list
// should also be "ThisZone"
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
if (pPortDesc->pd_DesiredZone != NULL)
AtalkZoneDereference(pPortDesc->pd_DesiredZone);
AtalkZoneReferenceByPtr(pPortDesc->pd_DesiredZone =
pPortDesc->pd_ZoneList->zl_pZone);
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
RetCode = TRUE;
}
break;
}
// We did not get an answer. We had better be able to seed. There is
// a chance that we got "ThisZone" set when allocating our node and
// whatever router told us that went down before we could ask for the
// zone list - so de-allocate the multicast address, if any first
if ((pPortDesc->pd_Flags & (PD_EXT_NET | PD_VALID_DESIRED_ZONE)) ==
(PD_EXT_NET | PD_VALID_DESIRED_ZONE))
{
if (!AtalkFixedCompareCaseSensitive(pPortDesc->pd_ZoneMulticastAddr,
pPortDesc->pd_BroadcastAddrLen,
pPortDesc->pd_BroadcastAddr,
pPortDesc->pd_BroadcastAddrLen))
(*pPortDesc->pd_RemoveMulticastAddr)(pPortDesc,
pPortDesc->pd_ZoneMulticastAddr,
FALSE,
NULL,
NULL);
}
// Now we better know enough to seed
if (pPortDesc->pd_InitialZoneList == NULL)
{
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipGetZoneListForPort: %sExt port, NULL InitialZoneList\n",
EXT_NET(pPortDesc) ? "" : "Non"));
break;
}
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipGetZoneListForPort: Moving Initial ZoneList to Current on port %Z\n",
&pPortDesc->pd_AdapterKey));
pPortDesc->pd_ZoneList = AtalkZoneCopyList(pPortDesc->pd_InitialZoneList);
if (EXT_NET(pPortDesc))
{
// We need to seed the default zone too
AtalkZoneReferenceByPtr(pPortDesc->pd_DefaultZone =
pPortDesc->pd_InitialDefaultZone);
pPortDesc->pd_Flags |= PD_VALID_DEFAULT_ZONE;
if (pPortDesc->pd_InitialDesiredZone != NULL)
{
AtalkZoneReferenceByPtr(pPortDesc->pd_DesiredZone =
pPortDesc->pd_InitialDesiredZone);
}
else
{
AtalkZoneReferenceByPtr(pPortDesc->pd_DesiredZone =
pPortDesc->pd_InitialDefaultZone);
}
// Finally set the zone multicast address
AtalkZipMulticastAddrForZone(pPortDesc,
pPortDesc->pd_DesiredZone->zn_Zone,
pPortDesc->pd_DesiredZone->zn_ZoneLen,
MulticastAddr);
if (!AtalkFixedCompareCaseSensitive(MulticastAddr,
pPortDesc->pd_BroadcastAddrLen,
pPortDesc->pd_BroadcastAddr,
pPortDesc->pd_BroadcastAddrLen))
{
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
(*pPortDesc->pd_AddMulticastAddr)(pPortDesc,
MulticastAddr,
FALSE,
NULL,
NULL);
ACQUIRE_SPIN_LOCK(&pPortDesc->pd_Lock, &OldIrql);
}
RtlCopyMemory(pPortDesc->pd_ZoneMulticastAddr,
MulticastAddr,
pPortDesc->pd_BroadcastAddrLen);
}
else
{
// On non-extended networks, this (desired/default)should be the
// only one on zone-list
AtalkZoneReferenceByPtr(pPortDesc->pd_DesiredZone =
pPortDesc->pd_ZoneList->zl_pZone);
}
pPortDesc->pd_Flags |= PD_VALID_DESIRED_ZONE;
RELEASE_SPIN_LOCK(&pPortDesc->pd_Lock, OldIrql);
RetCode = TRUE;
} while (FALSE);
AtalkRtmpDereferenceRte(pRte, FALSE);
if (pBuffDesc != NULL)
{
AtalkFreeBuffDesc(pBuffDesc);
}
return(RetCode);
}
/*** AtalkZipZoneReferenceByName
*
*/
PZONE
AtalkZoneReferenceByName(
IN PBYTE ZoneName,
IN BYTE ZoneLen
)
{
PZONE pZone;
BYTE Len;
KIRQL OldIrql;
ULONG i, Hash;
for (i = 0, Hash = 0, Len = ZoneLen;
Len > 0;
Len --, i++)
{
Hash <<= 1;
Hash += AtalkUpCaseTable[ZoneName[i]];
}
Hash %= NUM_ZONES_HASH_BUCKETS;
ACQUIRE_SPIN_LOCK(&AtalkZoneLock, &OldIrql);
for (pZone = AtalkZonesTable[Hash];
pZone != NULL;
pZone = pZone->zn_Next)
{
if (AtalkFixedCompareCaseInsensitive(ZoneName,
ZoneLen,
pZone->zn_Zone,
pZone->zn_ZoneLen))
{
pZone->zn_RefCount ++;
break;
}
}
if (pZone == NULL)
{
if ((pZone = (PZONE)AtalkAllocMemory(sizeof(ZONE) + ZoneLen)) != NULL)
{
pZone->zn_RefCount = 1;
pZone->zn_ZoneLen = ZoneLen;
RtlCopyMemory(pZone->zn_Zone, ZoneName, ZoneLen);
pZone->zn_Zone[ZoneLen] = 0;
AtalkLinkDoubleAtHead(AtalkZonesTable[Hash],
pZone,
zn_Next,
zn_Prev);
}
}
RELEASE_SPIN_LOCK(&AtalkZoneLock, OldIrql);
return(pZone);
}
/*** AtalkZipZoneReferenceByPtr
*
*/
VOID
AtalkZoneReferenceByPtr(
IN PZONE pZone
)
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkZoneLock, &OldIrql);
pZone->zn_RefCount++;
RELEASE_SPIN_LOCK(&AtalkZoneLock, OldIrql);
}
/*** AtalkZoneDereference
*
*/
VOID
AtalkZoneDereference(
IN PZONE pZone
)
{
KIRQL OldIrql;
ACQUIRE_SPIN_LOCK(&AtalkZoneLock, &OldIrql);
if (--pZone->zn_RefCount == 0)
{
AtalkUnlinkDouble(pZone, zn_Next, zn_Prev);
AtalkFreeMemory(pZone);
}
RELEASE_SPIN_LOCK(&AtalkZoneLock, OldIrql);
}
/*** AtalkZipZoneFreeList
*
*/
VOID
AtalkZoneFreeList(
IN PZONE_LIST pZoneList
)
{
PZONE_LIST pNextZone;
for (; pZoneList != NULL; pZoneList = pNextZone)
{
pNextZone = pZoneList->zl_Next;
AtalkZoneDereference(pZoneList->zl_pZone);
AtalkFreeMemory(pZoneList);
}
}
/*** AtalkZoneNameOnList
*
*/
BOOLEAN
AtalkZoneNameOnList(
IN PBYTE ZoneName,
IN BYTE ZoneLen,
IN PZONE_LIST pZoneList
)
{
for ( ; pZoneList != NULL; pZoneList = pZoneList->zl_Next)
if (AtalkFixedCompareCaseInsensitive(pZoneList->zl_pZone->zn_Zone,
pZoneList->zl_pZone->zn_ZoneLen,
ZoneName, ZoneLen))
return(TRUE);
return(FALSE);
}
/*** AtalkZipZoneOnList
*
*/
BOOLEAN
AtalkZoneOnList(
IN PZONE pZone,
IN PZONE_LIST pZoneList
)
{
for ( ; pZoneList != NULL; pZoneList = pZoneList->zl_Next)
if (pZoneList->zl_pZone == pZone)
return(TRUE);
return(FALSE);
}
/*** AtalkZipZone
*
*/
ULONG
AtalkZoneNumOnList(
IN PZONE_LIST pZoneList
)
{
ULONG Cnt;
for (Cnt = 0; pZoneList != NULL; pZoneList = pZoneList->zl_Next)
Cnt++;
return Cnt;
}
/*** AtalkZipZoneAddToList
*
*/
PZONE_LIST
AtalkZoneAddToList(
IN PZONE_LIST pZoneList,
IN PBYTE ZoneName,
IN BYTE ZoneLen
)
{
PZONE_LIST pNewZoneList;
PZONE pZone;
// Get memory for a new ZoneList node.
pNewZoneList = (PZONE_LIST)AtalkAllocMemory(sizeof(ZONE_LIST));
if (pNewZoneList != NULL)
{
if ((pZone = AtalkZoneReferenceByName(ZoneName, ZoneLen)) != NULL)
{
pNewZoneList->zl_Next = pZoneList;
pNewZoneList->zl_pZone = pZone;
}
else
{
AtalkZoneFreeList(pNewZoneList);
pNewZoneList = NULL;
}
}
else
{
AtalkZoneFreeList(pZoneList);
}
return(pNewZoneList);
}
/*** AtalkZipZoneCopyList
*
*/
PZONE_LIST
AtalkZoneCopyList(
IN PZONE_LIST pZoneList
)
{
PZONE_LIST pNewZoneList,
pZoneListStart = NULL,
*ppZoneList = &pZoneListStart;
for (; pZoneList != NULL; pZoneList = pZoneList = pZoneList->zl_Next)
{
pNewZoneList = AtalkAllocMemory(sizeof(ZONE_LIST));
if (pNewZoneList == NULL)
{
break;
}
pNewZoneList->zl_Next = NULL;
pNewZoneList->zl_pZone = pZoneList->zl_pZone;
*ppZoneList = pNewZoneList;
ppZoneList = &pNewZoneList->zl_Next;
AtalkZoneReferenceByPtr(pNewZoneList->zl_pZone);
}
if (pNewZoneList == NULL)
{
AtalkZoneFreeList(pZoneListStart);
}
return(pZoneListStart);
}
/*** atalkZipSendComplete
*
*/
VOID FASTCALL
atalkZipSendComplete(
IN NDIS_STATUS Status,
IN PSEND_COMPL_INFO pSendInfo
)
{
PBUFFER_DESC pBuffDesc = (PBUFFER_DESC)(pSendInfo->sc_Ctx1);
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_INFO,
("atalkZipSendComplete: Freeing BuffDesc %lx\n", pBuffDesc));
if (!ATALK_SUCCESS(Status))
DBGPRINT(DBG_COMP_ZIP, DBG_LEVEL_ERR,
("atalkZipSendComplete: Failed %lx, pBuffDesc %lx\n",
Status, pBuffDesc));
AtalkFreeBuffDesc(pBuffDesc);
}
#if DBG
VOID
AtalkZoneDumpTable(
VOID
)
{
int i;
PZONE pZone;
ACQUIRE_SPIN_LOCK_DPC(&AtalkZoneLock);
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL, ("ZONETABLE: \n"));
for (i = 0; i < NUM_ZONES_HASH_BUCKETS; i ++)
{
for (pZone = AtalkZonesTable[i]; pZone != NULL; pZone = pZone->zn_Next)
{
DBGPRINT(DBG_COMP_DUMP, DBG_LEVEL_FATAL,
("\t\t%s\n", pZone->zn_Zone));
}
}
RELEASE_SPIN_LOCK_DPC(&AtalkZoneLock);
}
#endif