windows-nt/Source/XPSP1/NT/net/atm/arp/atmarpc/arpif.c

3420 lines
76 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
arpif.c
Abstract:
ARP Interface Entry points. These are called (indirectly) by the IP
layer. All these entry points have the common prefix "AtmArpIf".
Revision History:
Who When What
-------- -------- ----------------------------------------------
arvindm 07-17-96 Created
Notes:
--*/
#include <precomp.h>
#define _FILENUMBER 'FIRA'
#if DBG_QRY
ULONG AaIgnoreInstance = 0;
#endif
IP_MASK AtmArpIPMaskTable[] =
{
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSA_MASK,
CLASSB_MASK,
CLASSB_MASK,
CLASSB_MASK,
CLASSB_MASK,
CLASSC_MASK,
CLASSC_MASK,
CLASSD_MASK,
CLASSE_MASK
};
VOID
AtmArpReStartInterface(
IN PNDIS_WORK_ITEM pWorkItem,
IN PVOID IfContext
);
#ifndef NEWARP
NDIS_STATUS
AtmArpInitIPInterface(
VOID
)
/*++
Routine Description:
Initialize our interface with IP. This consists of querying IP for
its "Add Interface" and "Delete Interface" entry points.
Arguments:
None. It is assumed that the caller has a lock to the ATMARP Global
Info structure.
Return Value:
NDIS_STATUS_SUCCESS if initialization was successful
NDIS_STATUS_XXX error code otherwise.
--*/
{
NDIS_STATUS Status;
#if !LINK_WITH_IP
IP_GET_PNP_ARP_POINTERS IPInfo;
UNICODE_STRING IPDeviceName;
PIRP pIrp;
PFILE_OBJECT pIpFileObject;
PDEVICE_OBJECT pIpDeviceObject;
IO_STATUS_BLOCK ioStatusBlock;
//
// Initialize.
//
pIrp = (PIRP)NULL;
pIpFileObject = (PFILE_OBJECT)NULL;
pIpDeviceObject = (PDEVICE_OBJECT)NULL;
do
{
NdisInitUnicodeString(&IPDeviceName, DD_IP_DEVICE_NAME);
//
// Get the file and device objects for the IP device.
//
Status = IoGetDeviceObjectPointer(
&IPDeviceName,
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
&pIpFileObject,
&pIpDeviceObject);
if ((Status != STATUS_SUCCESS) || (pIpDeviceObject == NULL))
{
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Reference the device object.
//
ObReferenceObject(pIpDeviceObject);
pIrp = IoBuildDeviceIoControlRequest(IOCTL_IP_GET_PNP_ARP_POINTERS,
pIpDeviceObject,
NULL,
0,
&IPInfo,
sizeof (IPInfo),
FALSE,
NULL,
&ioStatusBlock);
if (pIrp == NULL)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
Status = IoCallDriver(pIpDeviceObject, pIrp);
if (Status != STATUS_SUCCESS)
{
Status = NDIS_STATUS_FAILURE;
break;
}
pAtmArpGlobalInfo->pIPAddInterfaceRtn = IPInfo.IPAddInterface;
pAtmArpGlobalInfo->pIPDelInterfaceRtn = IPInfo.IPDelInterface;
Status = NDIS_STATUS_SUCCESS;
}
while (FALSE);
if (pIpFileObject != (PFILE_OBJECT)NULL)
{
//
// Dereference the file object
//
ObDereferenceObject((PVOID)pIpFileObject);
}
if (pIpDeviceObject != (PDEVICE_OBJECT)NULL)
{
//
// Close the device.
//
ObDereferenceObject((PVOID)pIpDeviceObject);
}
#else
pAtmArpGlobalInfo->pIPAddInterfaceRtn = IPAddInterface;
pAtmArpGlobalInfo->pIPDelInterfaceRtn = (IPDelInterfacePtr)IPDelInterface;
Status = NDIS_STATUS_SUCCESS;
#endif // !LINK_WITH_IP
AADEBUGP(AAD_INFO, ("Init IP Interface: returning Status 0x%x\n", Status));
return (Status);
}
INT
AtmArpIfDynRegister(
IN PNDIS_STRING pAdapterString,
IN PVOID IPContext,
IN IPRcvRtn IPRcvHandler,
IN IPTxCmpltRtn IPTxCmpltHandler,
IN IPStatusRtn IPStatusHandler,
IN IPTDCmpltRtn IPTDCmpltHandler,
IN IPRcvCmpltRtn IPRcvCmpltHandler,
IN struct LLIPBindInfo *pBindInfo,
IN UINT NumIFBound
)
/*++
Routine Description:
This routine is called from the IP layer when it wants to tell us,
the ARP module, about its handlers for an Interface.
Arguments:
pAdapterString - Name of the logical adapter for this interface
IPContext - IP's context for this interface
IPRcvHandler - Up-call for receives
IPTxCmpltHandler - Up-call for transmit completes
IPStatusHandler - Up-call to indicate status changes
IPTDCmpltHandler - Up-call to indicate completion of Transfer-Data
IPRcvCmpltHandler - Up-call to indicate temporary completion of receives
pBindInfo - Pointer to bind info with our information
NumIFBound - Count for this interface
Return Value:
(UINT)TRUE always.
--*/
{
PATMARP_INTERFACE pInterface;
pInterface = (PATMARP_INTERFACE)(pBindInfo->lip_context);
AA_STRUCT_ASSERT(pInterface, aai);
AADEBUGP(AAD_INFO, ("IfDynRegister: pIf 0x%x\n", pInterface));
pInterface->IPContext = IPContext;
pInterface->IPRcvHandler = IPRcvHandler;
pInterface->IPTxCmpltHandler = IPTxCmpltHandler;
pInterface->IPStatusHandler = IPStatusHandler;
pInterface->IPTDCmpltHandler = IPTDCmpltHandler;
pInterface->IPRcvCmpltHandler = IPRcvCmpltHandler;
pInterface->IFIndex = NumIFBound;
return ((UINT)TRUE);
}
#else
// NEWARP
INT
AtmArpIfDynRegister(
IN PNDIS_STRING pAdapterString,
IN PVOID IPContext,
IN struct _IP_HANDLERS * pIpHandlers,
IN struct LLIPBindInfo * pBindInfo,
IN UINT InterfaceNumber
)
/*++
Routine Description:
This routine is called from the IP layer when it wants to tell us,
the ARP module, about its handlers for an Interface.
Arguments:
pAdapterString - Name of the logical adapter for this interface
IPContext - IP's context for this interface
pIpHandlers - Points to struct containing the following handlers:
IPRcvHandler - Up-call for receives
IPTxCmpltHandler - Up-call for transmit completes
IPStatusHandler - Up-call to indicate status changes
IPTDCmpltHandler - Up-call to indicate completion of Transfer-Data
IPRcvCmpltHandler - Up-call to indicate temporary completion of receives
pBindInfo - Pointer to bind info with our information
InterfaceNumber - ID for this interface
Return Value:
(UINT)TRUE always.
--*/
{
PATMARP_INTERFACE pInterface;
pInterface = (PATMARP_INTERFACE)(pBindInfo->lip_context);
AA_STRUCT_ASSERT(pInterface, aai);
AADEBUGP(AAD_INFO, ("IfDynRegister: pIf 0x%x\n", pInterface));
pInterface->IPContext = IPContext;
pInterface->IPRcvHandler = pIpHandlers->IpRcvHandler;
pInterface->IPTxCmpltHandler = pIpHandlers->IpTxCompleteHandler;
pInterface->IPStatusHandler = pIpHandlers->IpStatusHandler;
pInterface->IPTDCmpltHandler = pIpHandlers->IpTransferCompleteHandler;
pInterface->IPRcvCmpltHandler = pIpHandlers->IpRcvCompleteHandler;
#ifdef _PNP_POWER_
pInterface->IPPnPEventHandler = pIpHandlers->IpPnPHandler;
pInterface->IPRcvPktHandler = pIpHandlers->IpRcvPktHandler;
#endif // _PNP_POWER_
pInterface->IFIndex = InterfaceNumber;
return ((UINT)TRUE);
}
#endif // !NEWARP
VOID
AtmArpIfOpen(
IN PVOID Context
)
/*++
Routine Description:
This routine is called when IP is ready to use this interface.
This is equivalent to setting AdminState to UP.
We register our SAP with the Call Manager, thus allowing incoming
calls to reach us. If atleast one local IP address has been set,
and the ATM interface is ip, we start registering ourselves with
the server.
Arguments:
Context - Actually a pointer to our ATMARP Interface structure
Return Value:
None
--*/
{
PATMARP_INTERFACE pInterface;
NDIS_HANDLE ProtocolSapContext;
PNDIS_HANDLE pNdisSapHandle;
PCO_SAP pSap;
BOOLEAN AtmInterfaceDown;
#if DBG
AA_IRQL EntryIrq, ExitIrq;
#endif
AA_GET_ENTRY_IRQL(EntryIrq);
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
AADEBUGP(AAD_INFO, ("IfOpen: pIf 0x%x\n", pInterface));
AA_ACQUIRE_IF_LOCK(pInterface);
AA_ASSERT(pInterface->NdisAfHandle != NULL);
pInterface->AdminState = IF_STATUS_UP;
AA_INIT_BLOCK_STRUCT(&(pInterface->Block));
AtmInterfaceDown = !(pInterface->AtmInterfaceUp);
AA_RELEASE_IF_LOCK(pInterface);
//
// Get the local ATM address if we haven't got it yet.
//
if (AtmInterfaceDown)
{
AtmArpGetAtmAddress(pInterface);
}
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
//
// Register our SAP(s) with the Call Manager.
//
AtmArpRegisterSaps(pInterface);
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
#ifdef ATMARP_WMI
//
// Make this interface a WMI provider.
//
AtmArpWmiInitInterface(pInterface, AtmArpGuidList, AtmArpGuidCount);
#endif // ATMARP_WMI
AA_ACQUIRE_IF_LOCK(pInterface);
#ifdef IPMCAST
//
// Start multicast registration with MARS.
//
AtmArpMcStartRegistration(pInterface);
//
// IF lock is released within the above.
//
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
AA_ACQUIRE_IF_LOCK(pInterface);
#endif // IPMCAST
//
// All necessary pre-conditions are checked within
// AtmArpStartRegistration.
//
AtmArpStartRegistration(pInterface);
//
// IF lock is released within the above.
//
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
return;
}
VOID
AtmArpIfClose(
IN PVOID Context
)
/*++
Routine Description:
IP wants to stop using this Interface. We assume that this is called
in response to our Up-call to IP's DelInterface entry point.
We simply dereference the interface, unless we are actually in the process
of bringing it down and up due to a reconfigure notification.
Arguments:
Context - Actually a pointer to our ATMARP Interface structure
Return Value:
None
--*/
{
PATMARP_INTERFACE pInterface;
ULONG rc; // Ref Count
#if DBG
AA_IRQL EntryIrq, ExitIrq;
#endif
BOOLEAN fQueueRestart = FALSE;
PNDIS_WORK_ITEM pWorkItem;
NDIS_STATUS NdisStatus;
AA_GET_ENTRY_IRQL(EntryIrq);
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
AA_ACQUIRE_IF_LOCK(pInterface);
//
// Ensure that we won't send up an IPDelInterface on this
// interface.
//
pInterface->IPContext = NULL;
if (pInterface->ReconfigState==RECONFIG_SHUTDOWN_PENDING)
{
AA_ALLOC_MEM(pWorkItem, NDIS_WORK_ITEM, sizeof(NDIS_WORK_ITEM));
if (pWorkItem == NULL)
{
AA_ASSERT(FALSE);
}
else
{
pInterface->ReconfigState=RECONFIG_RESTART_QUEUED;
fQueueRestart = TRUE;
}
}
else
{
AA_ASSERT(pInterface->ReconfigState==RECONFIG_NOT_IN_PROGRESS);
}
AA_RELEASE_IF_LOCK(pInterface);
#ifdef ATMARP_WMI
//
// Deregister this Interface as a WMI provider.
// We do this even when bringing down the interface for a reconfig
// because certain IP information could potentially become stale.
//
AtmArpWmiShutdownInterface(pInterface);
#endif // ATMARP_WMI
if (fQueueRestart)
{
//
// We have a request to reconfigure this interface. So we will
// keep this structure allocated and queue
// a work item to bring this interface back up -- reading the latest
// configuration paramters from the registry.
//
//
// We do not strictly need to reference the interface here because we
// expect the interface to be still around. Nevertheless
// we reference it here and dereference it when the work item fires.
//
AtmArpReferenceInterface(pInterface); // ReStart Work Item
NdisInitializeWorkItem(
pWorkItem,
AtmArpReStartInterface,
(PVOID)pInterface
);
NdisStatus = NdisScheduleWorkItem(pWorkItem);
if (NdisStatus != NDIS_STATUS_SUCCESS)
{
//
// Ouch, fall back to simply deleting the interface.
//
AA_FREE_MEM(pWorkItem);
fQueueRestart = FALSE;
}
}
if (!fQueueRestart)
{
AADEBUGP(AAD_INFO, ("IfClose: will deallocate pIf 0x%x, RefCount %d\n",
pInterface, pInterface->RefCount));
AA_ACQUIRE_IF_LOCK(pInterface);
rc = AtmArpDereferenceInterface(pInterface);
if (rc != 0)
{
AA_RELEASE_IF_LOCK(pInterface);
}
//
// else the Interface is gone.
//
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
}
return;
}
UINT
AtmArpIfAddAddress(
IN PVOID Context,
IN UINT AddressType,
IN IP_ADDRESS IPAddress,
IN IP_MASK Mask
#ifndef BUILD_FOR_1381
,
IN PVOID Context2
#endif // BUILD_FOR_1381
)
/*++
Routine Description:
The IP layer calls this when a new IP address (or block of IP addresses,
as determined by AddressType) needs to be added to an Interface.
We could see any of four address types: Local, Multicast, Broadcast
and Proxy ARP. In the case of Proxy ARP, the address along with the mask
can specify a block of contiguous IP addresses for which this host acts
as a proxy. Currently, we only support the "Local", "Broadcast", and
"Multicast" types.
If we just added the only local address for this interface, and the
ATM interface is up, and AdminState for this interface is UP, we initiate
address registration with the ARP server.
Arguments:
Context - Actually a pointer to the ATMARP Interface structure
AddressType - Type of address(es) being added.
IPAddress - Address to be added.
Mask - For the above.
Context2 - Additional context (for what?)
Return Value:
(UINT)TRUE if successful, (UINT)FALSE otherwise.
--*/
{
PATMARP_INTERFACE pInterface;
PIP_ADDRESS_ENTRY pIpAddressEntry;
UINT ReturnStatus;
BOOLEAN LockAcquired;
ReturnStatus = (UINT)FALSE; // Initialize to Failure
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
AA_ACQUIRE_IF_LOCK(pInterface);
LockAcquired = TRUE;
if (AddressType == LLIP_ADDR_LOCAL)
{
//
// Find a place to put this new address in.
//
if (pInterface->NumOfIPAddresses == 0)
{
pIpAddressEntry = &(pInterface->LocalIPAddress);
}
else
{
AA_ALLOC_MEM(pIpAddressEntry, IP_ADDRESS_ENTRY, sizeof(IP_ADDRESS_ENTRY));
if (pIpAddressEntry != (PIP_ADDRESS_ENTRY)NULL)
{
pIpAddressEntry->pNext = pInterface->LocalIPAddress.pNext;
pInterface->LocalIPAddress.pNext = pIpAddressEntry;
}
}
if (pIpAddressEntry != (PIP_ADDRESS_ENTRY)NULL)
{
ReturnStatus = (UINT)TRUE;
pIpAddressEntry->IPAddress = IPAddress;
pIpAddressEntry->IPMask = Mask;
pIpAddressEntry->IsRegistered = FALSE;
pIpAddressEntry->IsFirstRegistration = TRUE;
pInterface->NumOfIPAddresses++;
if (pInterface->NumOfIPAddresses == 1)
{
AtmArpStartRegistration(pInterface);
//
// IF Lock is released by above routine.
//
LockAcquired = FALSE;
}
else
{
if (AA_IS_FLAG_SET(
pInterface->Flags,
AA_IF_SERVER_STATE_MASK,
AA_IF_SERVER_REGISTERED) &&
(!pInterface->PVCOnly))
{
AA_RELEASE_IF_LOCK(pInterface);
LockAcquired = FALSE;
AtmArpSendARPRequest(
pInterface,
&IPAddress,
&IPAddress
);
}
//
// else either
// (a) registration is in progress; at the end of it,
// we will register all unregistered IP addresses.
// or
// (b) we are in a PVC only environment, no ARP server.
//
}
}
//
// else allocation failure -- fall thru
//
}
#ifdef IPMCAST
else if ((AddressType == LLIP_ADDR_BCAST) || (AddressType == LLIP_ADDR_MCAST))
{
if (AddressType == LLIP_ADDR_BCAST)
{
pInterface->BroadcastAddress = IPAddress;
}
ReturnStatus = AtmArpMcAddAddress(pInterface, IPAddress, Mask);
//
// IF Lock is released within the above.
//
LockAcquired = FALSE;
}
#else
else if (AddressType == LLIP_ADDR_BCAST)
{
pInterface->BroadcastAddress = IPAddress;
ReturnStatus = (UINT)TRUE;
}
#endif // IPMCAST
if (LockAcquired)
{
AA_RELEASE_IF_LOCK(pInterface);
}
#ifdef BUILD_FOR_1381
AADEBUGP(AAD_INFO,
("IfAddAddress: IF 0x%x, Type %d, Addr %d.%d.%d.%d, Mask 0x%x, Ret %d\n",
pInterface,
AddressType,
((PUCHAR)(&IPAddress))[0],
((PUCHAR)(&IPAddress))[1],
((PUCHAR)(&IPAddress))[2],
((PUCHAR)(&IPAddress))[3],
Mask, ReturnStatus));
#else
AADEBUGP(AAD_INFO,
("IfAddAddress: IF 0x%x, Type %d, Addr %d.%d.%d.%d, Mask 0x%x, Ret %d, Ctx2 0x%x\n",
pInterface,
AddressType,
((PUCHAR)(&IPAddress))[0],
((PUCHAR)(&IPAddress))[1],
((PUCHAR)(&IPAddress))[2],
((PUCHAR)(&IPAddress))[3],
Mask, ReturnStatus, Context2));
#endif // BUILD_FOR_1381
return (ReturnStatus);
}
UINT
AtmArpIfDelAddress(
IN PVOID Context,
IN UINT AddressType,
IN IP_ADDRESS IPAddress,
IN IP_MASK Mask
)
/*++
Routine Description:
This is called from the IP layer when an address added via AtmArpIfAddAddress
is to be deleted.
Currently, only the "Local" Address type is supported.
Assumption: the given address was successfully added earlier.
Arguments:
Context - Actually a pointer to the ATMARP Interface structure
AddressType - Type of address(es) being deleted.
IPAddress - Address to be deleted.
Mask - For the above.
Return Value:
(UINT)TRUE if successful, (UINT)FALSE otherwise.
--*/
{
PATMARP_INTERFACE pInterface;
PIP_ADDRESS_ENTRY pIpAddressEntry;
PIP_ADDRESS_ENTRY pPrevIpAddressEntry;
PIP_ADDRESS_ENTRY pTmpIpAddressEntry;
UINT ReturnValue;
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
if (AddressType == LLIP_ADDR_LOCAL)
{
AA_ACQUIRE_IF_LOCK(pInterface);
//
// Search for the entry to be deleted.
//
pPrevIpAddressEntry = (PIP_ADDRESS_ENTRY)NULL;
pIpAddressEntry = &(pInterface->LocalIPAddress);
while (!IP_ADDR_EQUAL(pIpAddressEntry->IPAddress, IPAddress))
{
pPrevIpAddressEntry = pIpAddressEntry;
pIpAddressEntry = pIpAddressEntry->pNext;
AA_ASSERT(pIpAddressEntry != (PIP_ADDRESS_ENTRY)NULL);
}
//
// If it was the only one in the list, there is nothing
// to be done. Otherwise, update the list.
//
if (pInterface->NumOfIPAddresses > 1)
{
//
// More than one entry existed. Check if we deleted the
// first one.
//
if (pPrevIpAddressEntry == (PIP_ADDRESS_ENTRY)NULL)
{
//
// Copy in the contents of the second entry
// into the head of the list, and delete the
// second entry.
//
AA_ASSERT(pIpAddressEntry == &(pInterface->LocalIPAddress));
AA_ASSERT(pIpAddressEntry->pNext != (PIP_ADDRESS_ENTRY)NULL);
pIpAddressEntry->IPAddress = pIpAddressEntry->pNext->IPAddress;
pIpAddressEntry->IPMask = pIpAddressEntry->pNext->IPMask;
pTmpIpAddressEntry = pIpAddressEntry->pNext;
pIpAddressEntry->pNext = pIpAddressEntry->pNext->pNext;
pIpAddressEntry = pTmpIpAddressEntry;
}
else
{
pPrevIpAddressEntry->pNext = pIpAddressEntry->pNext;
}
AA_FREE_MEM(pIpAddressEntry);
}
pInterface->NumOfIPAddresses--;
AA_RELEASE_IF_LOCK(pInterface);
ReturnValue = (UINT)TRUE;
}
else
#ifdef IPMCAST
{
if ((AddressType == LLIP_ADDR_BCAST) || (AddressType == LLIP_ADDR_MCAST))
{
AA_ACQUIRE_IF_LOCK(pInterface);
ReturnValue = AtmArpMcDelAddress(pInterface, IPAddress, Mask);
}
else
{
ReturnValue = (UINT)FALSE;
}
}
#else
{
ReturnValue = (UINT)FALSE;
}
#endif // IPMCAST
AADEBUGP(AAD_INFO,
("IfDelAddress: Ctxt 0x%x, Type 0x%x, IPAddr 0x%x, Mask 0x%x, Ret %d\n",
Context, AddressType, IPAddress, Mask, ReturnValue));
return (ReturnValue);
}
#ifdef NEWARP
NDIS_STATUS
AtmArpIfMultiTransmit(
IN PVOID Context,
IN PNDIS_PACKET * pNdisPacketArray,
IN UINT NumberOfPackets,
IN IP_ADDRESS Destination,
IN RouteCacheEntry * pRCE OPTIONAL
#if P2MP
,
IN void * ArpCtxt
#endif
)
/*++
Routine Description:
This is called from the IP layer when it has a sequence of datagrams,
each in the form of an NDIS buffer chain, to send over an Interface.
Arguments:
Context - Actually a pointer to our Interface structure
pNdisPacketArray - Array of Packets to be sent on this Interface
NumberOfPackets - Length of array
Destination - IP address of next hop for this packet
pRCE - Optional pointer to Route Cache Entry structure.
Return Value:
NDIS_STATUS_PENDING if all packets were queued for transmission.
If one or more packets "failed", we set the packet status to reflect
what happened to each, and return NDIS_STATUS_FAILURE.
--*/
{
NDIS_STATUS Status;
PNDIS_PACKET * ppNdisPacket;
for (ppNdisPacket = pNdisPacketArray;
NumberOfPackets > 0;
NumberOfPackets--, ppNdisPacket++)
{
PNDIS_PACKET pNdisPacket;
pNdisPacket = *ppNdisPacket;
NDIS_SET_PACKET_STATUS(pNdisPacket, NDIS_STATUS_PENDING);
#if DBG
AA_ASSERT(pNdisPacket->Private.Head != NULL);
#endif // DBG
Status = AtmArpIfTransmit(
Context,
*ppNdisPacket,
Destination,
pRCE
#if P2MP
,NULL
#endif
);
if (Status != NDIS_STATUS_PENDING)
{
NDIS_SET_PACKET_STATUS(*ppNdisPacket, Status);
break;
}
}
return (Status);
}
#endif // NEWARP
NDIS_STATUS
AtmArpIfTransmit(
IN PVOID Context,
IN PNDIS_PACKET pNdisPacket,
IN IP_ADDRESS Destination,
IN RouteCacheEntry * pRCE OPTIONAL
#if P2MP
,
IN void * ArpCtxt
#endif
)
/*++
Routine Description:
This is called from the IP layer when it has a datagram (in the form of
an NDIS buffer chain) to send over an Interface.
The destination IP address is passed to us in this routine, which may
or may not be the final destination for the packet.
The Route Cache Entry is created by the IP layer, and is used to speed
up our lookups. An RCE, if specified, uniquely identifies atleast the
IP destination for this packet. The RCE contains space for the ARP layer
to keep context information about this destination. When the first packet
goes out to a Destination, our context info in the RCE will be NULL, and
we search the ARP Table for the matching IP Entry. However, we then fill
our context info (pointer to IP Entry) in the RCE, so that subsequent
transmits aren't slowed down by an IP address lookup.
Arguments:
Context - Actually a pointer to our Interface structure
pNdisPacket - Packet to be sent on this Interface
Destination - IP address of next hop for this packet
pRCE - Optional pointer to Route Cache Entry structure.
Return Value:
Status of the transmit: NDIS_STATUS_SUCCESS, NDIS_STATUS_PENDING, or
a failure.
--*/
{
PATMARP_INTERFACE pInterface;
PATMARP_IP_ENTRY pIpEntry; // IP Entry corresp to Destination
PATMARP_ATM_ENTRY pAtmEntry; // ATM Entry for this destination
PATMARP_RCE_CONTEXT pRCEContext; // Our context in the RCE
PATMARP_FLOW_INFO pFlowInfo; // Flow to which this packet belongs
PATMARP_FILTER_SPEC pFilterSpec; // Filter Spec for this packet
PATMARP_FLOW_SPEC pFlowSpec; // Flow Spec for this packet
PNDIS_BUFFER pHeaderBuffer; // NDIS Buffer for LLC/SNAP header
PUCHAR pHeader; // Pointer to header area
NDIS_STATUS Status; // Return value
BOOLEAN IsBroadcastAddress;
BOOLEAN CreateNewEntry; // Should we create a new IP entry?
#ifdef IPMCAST
BOOLEAN NeedMcRevalidation; // If Multicast, do we revalidate?
#endif // IPMCAST
ULONG rc;
#if DBG
AA_IRQL EntryIrq, ExitIrq;
#endif
AA_GET_ENTRY_IRQL(EntryIrq);
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
AADEBUGP(AAD_EXTRA_LOUD,
("IfTransmit: pIf 0x%x, Pkt 0x%x, Dst 0x%x, pRCE 0x%x\n",
pInterface, pNdisPacket, Destination, pRCE));
#if DBG
if (AaDataDebugLevel & (AAD_DATA_OUT|AAD_TRACK_BIG_SENDS))
{
ULONG TotalLength;
PNDIS_BUFFER pNdisBuffer;
NdisQueryPacket(
pNdisPacket,
NULL,
NULL,
NULL,
&TotalLength
);
if (AaDataDebugLevel & AAD_DATA_OUT)
{
AADEBUGP(AAD_WARNING, ("%d (", TotalLength));
for (pNdisBuffer = pNdisPacket->Private.Head;
pNdisBuffer != NULL;
pNdisBuffer = pNdisBuffer->Next)
{
INT BufLength;
NdisQueryBuffer(pNdisBuffer, NULL, &BufLength);
AADEBUGP(AAD_WARNING, (" %d", BufLength));
}
AADEBUGP(AAD_WARNING, (") => %d.%d.%d.%d\n",
(ULONG)(((PUCHAR)&Destination)[0]),
(ULONG)(((PUCHAR)&Destination)[1]),
(ULONG)(((PUCHAR)&Destination)[2]),
(ULONG)(((PUCHAR)&Destination)[3])));
}
if ((AaDataDebugLevel & AAD_TRACK_BIG_SENDS) && ((INT)TotalLength > AadBigDataLength))
{
AADEBUGP(AAD_WARNING, ("%d => %d.%d.%d.%d\n",
TotalLength,
(ULONG)(((PUCHAR)&Destination)[0]),
(ULONG)(((PUCHAR)&Destination)[1]),
(ULONG)(((PUCHAR)&Destination)[2]),
(ULONG)(((PUCHAR)&Destination)[3])));
DbgBreakPoint();
}
}
#endif // DBG
#ifdef PERF
AadLogSendStart(pNdisPacket, (ULONG)Destination, (PVOID)pRCE);
#endif // PERF
#ifdef IPMCAST
NeedMcRevalidation = FALSE;
#endif // IPMCAST
do
{
//
// Discard this packet if the AdminStatus for this interface
// is not UP.
//
if (pInterface->AdminState != IF_STATUS_UP)
{
Status = NDIS_STATUS_INTERFACE_DOWN;
break;
}
//
// Get the filter and flow specs for this packet.
//
AA_GET_PACKET_SPECS(pInterface, pNdisPacket, &pFlowInfo, &pFlowSpec, &pFilterSpec);
#ifdef GPC_MAYBE
//
// We may not do this stuff because there are things to be done
// (see multicast case below) with the IP entry that would be
// missed out if we do this.
//
pVc = AA_GET_VC_FOR_FLOW(pFlowInfo);
if (pVc != NULL_PATMARP_VC)
{
AA_ACQUIRE_VC_LOCK(pVc);
if ((pVc->FlowHandle == pFlowInfo) &&
AA_IS_FLAG_SET(pVc->Flags,
AA_VC_CALL_STATE_MASK,
AA_VC_CALL_STATE_ACTIVE) &&
!AA_IS_VC_GOING_DOWN(pVc)
)
{
AA_PREPARE_HEADER(pNdisPacket, pInterface, pFlowSpec, &Status);
if (Status == NDIS_STATUS_SUCCESS)
{
AtmArpRefreshTimer(&(pVc->Timer));
AtmArpReferenceVc(pVc); // IfTransmit
pVc->OutstandingSends++; // IfTransmit
NdisVcHandle = pVc->NdisVcHandle;
AA_RELEASE_VC_LOCK(pVc);
NDIS_CO_SEND(
NdisVcHandle,
&pNdisPacket,
1
);
break;
}
}
AA_RELEASE_VC_LOCK(pVc);
//
// Fall through
//
}
#endif // GPC
//
// Get the IP Entry for this destination: see if we have
// cached information that we can use.
//
if (pRCE != (RouteCacheEntry *)NULL)
{
pRCEContext = (PATMARP_RCE_CONTEXT)(pRCE->rce_context);
AA_ACQUIRE_IF_TABLE_LOCK(pInterface);
pIpEntry = pRCEContext->pIpEntry;
AADEBUGP(AAD_EXTRA_LOUD,
("Transmit: Dst 0x%x, RCE 0x%x, RCECntxt 0x%x, IPEntry 0x%x\n",
Destination, pRCE, pRCEContext, pIpEntry));
if (pIpEntry != NULL_PATMARP_IP_ENTRY)
{
AA_STRUCT_ASSERT(pIpEntry, aip);
AA_RELEASE_IF_TABLE_LOCK(pInterface);
AA_ACQUIRE_IE_LOCK(pIpEntry);
AA_ASSERT(AA_IE_IS_ALIVE(pIpEntry));
if (IP_ADDR_EQUAL(pIpEntry->IPAddress, Destination))
{
//
// The Route Cache points to the right IP Entry.
// Either send this packet, or queue it, and get out.
//
//
// Check if this IP Address has been resolved to an ATM address,
// and is "clean" (not aged out).
//
if (AA_IS_FLAG_SET(
pIpEntry->Flags,
AA_IP_ENTRY_STATE_MASK,
AA_IP_ENTRY_RESOLVED))
{
ULONG rc;
AA_ASSERT(pIpEntry->pAtmEntry != NULL_PATMARP_ATM_ENTRY);
pAtmEntry = pIpEntry->pAtmEntry;
AA_ACQUIRE_AE_LOCK_DPC(pAtmEntry);
AA_REF_AE(pAtmEntry, AE_REFTYPE_TMP);// Temp ref: IfTransmit1
AA_RELEASE_AE_LOCK_DPC(pAtmEntry);
#ifdef IPMCAST
if (AA_IS_FLAG_SET(
pIpEntry->Flags,
AA_IP_ENTRY_MC_VALIDATE_MASK,
AA_IP_ENTRY_MC_REVALIDATE))
{
AA_SET_FLAG(pIpEntry->Flags,
AA_IP_ENTRY_MC_VALIDATE_MASK,
AA_IP_ENTRY_MC_REVALIDATING);
NeedMcRevalidation = TRUE;
}
#endif // IPMCAST
IsBroadcastAddress = AA_IS_FLAG_SET(pIpEntry->Flags,
AA_IP_ENTRY_ADDR_TYPE_MASK,
AA_IP_ENTRY_ADDR_TYPE_NUCAST);
AA_RELEASE_IE_LOCK(pIpEntry);
AA_ACQUIRE_AE_LOCK(pAtmEntry);
Status = AtmArpSendPacketOnAtmEntry(
pInterface,
pAtmEntry,
pNdisPacket,
pFlowSpec,
pFilterSpec,
pFlowInfo,
IsBroadcastAddress
);
//
// The ATM Entry lock is released within the above.
// Get rid of the temp ref:
//
AA_ACQUIRE_AE_LOCK(pAtmEntry);
rc = AA_DEREF_AE(pAtmEntry, AE_REFTYPE_TMP);// Temp ref: IfTransmit1
if (rc != 0)
{
AA_RELEASE_AE_LOCK(pAtmEntry);
}
break; // goto end of processing
}
else
{
//
// We don't have the ATM address yet, but we have an
// IP Entry for the Destination IP address. Queue this
// packet on the IP Entry, and start Address resolution
// if not already started.
//
// But first, a check to avoid starting address resolution
// in a PVC-only environment.
//
if (pInterface->PVCOnly && (pIpEntry->pAtmEntry == NULL))
{
//
// This can happen if we had an active PVC and
// had learnt an IP address via InARP, and then
// the user had deleted the PVC. We would then be
// left with an IP entry, but no matching ATM entry.
// Abort this entry now.
//
AADEBUGP(AAD_FATAL,
("IfTransmit (PVC 1): IPEntry %x, Ref %d, Flags %x has NULL ATM Entry\n",
pIpEntry, pIpEntry->RefCount, pIpEntry->Flags));
AtmArpAbortIPEntry(pIpEntry);
//
// IP Entry lock is released above.
//
Status = NDIS_STATUS_SUCCESS;
break;
}
Status = AtmArpQueuePacketOnIPEntry(
pIpEntry,
pNdisPacket
);
//
// The IP Entry lock is released within the above.
//
break; // goto end of processing
}
// NOTREACHED
}
else
{
//
// The cache entry points to the wrong IP Entry. Invalidate
// the cache entry, and continue to the hard road.
//
AADEBUGP(AAD_INFO,
("IfTransmit: RCE (0x%x) points to wrong IP Entry (0x%x: %d.%d.%d.%d)\n",
pRCE,
pIpEntry,
((PUCHAR)(&(pIpEntry->IPAddress)))[0],
((PUCHAR)(&(pIpEntry->IPAddress)))[1],
((PUCHAR)(&(pIpEntry->IPAddress)))[2],
((PUCHAR)(&(pIpEntry->IPAddress)))[3]
));
AADEBUGP(AAD_INFO,
("RCE/IP Entry mismatch: Destn IP: %d.%d.%d.%d\n",
((PUCHAR)&Destination)[0],
((PUCHAR)&Destination)[1],
((PUCHAR)&Destination)[2],
((PUCHAR)&Destination)[3]
));
if (AtmArpUnlinkRCE(pRCE, pIpEntry))
{
ULONG rc; // Ref Count for IP Entry
//
// The IP Entry did have this RCE in its list.
//
rc = AA_DEREF_IE(pIpEntry, IE_REFTYPE_RCE); // RCE ref
if (rc > 0)
{
AA_RELEASE_IE_LOCK(pIpEntry);
}
// else the IP Entry is gone
}
else
{
//
// The IP Entry does not have this RCE in its list.
//
AA_RELEASE_IE_LOCK(pIpEntry);
}
//
// Continue processing below.
//
} // else -- if (RCE points to the right IP Entry)
} // if (RCE points to non-NULL IP Entry)
else
{
AA_RELEASE_IF_TABLE_LOCK(pInterface);
//
// Continue processing below
//
}
}
AA_ACQUIRE_IF_LOCK(pInterface);
IsBroadcastAddress = AtmArpIsBroadcastIPAddress(Destination, pInterface);
AA_RELEASE_IF_LOCK(pInterface);
#if DHCP_OVER_ATM
//
// Handle Broadcast packets separately.
//
if (IsBroadcastAddress)
{
Status = AtmArpSendBroadcast(
pInterface,
pNdisPacket,
pFlowSpec,
pFilterSpec
);
break;
}
#endif // DHCP_OVER_ATM
#ifdef IPMCAST
if (IsBroadcastAddress)
{
AAMCDEBUGP(AAD_EXTRA_LOUD,
("IfTransmit: pIf 0x%x, to Broadcast addr: %d.%d.%d.%d\n",
pInterface,
((PUCHAR)&Destination)[0],
((PUCHAR)&Destination)[1],
((PUCHAR)&Destination)[2],
((PUCHAR)&Destination)[3]));
if (pInterface->MARSList.ListSize == 0)
{
//
// Drop this packet.
//
Status = NDIS_STATUS_FAILURE;
break;
}
//
// Make sure that we send all IP *broadcast* packets to
// the All 1's group.
//
#ifdef MERGE_BROADCASTS
Destination = pInterface->BroadcastAddress;
#else
if (!CLASSD_ADDR(Destination))
{
Destination = pInterface->BroadcastAddress;
}
#endif // MERGE_BROADCASTS
}
#endif // IPMCAST
//
// No Route Cache Entry: search for the IP Entry the hard way.
// NOTE: if we are running PVCs only, we won't create a new
// IP entry here: the only way a new IP Entry is created is
// when we learn the IP+ATM info of the station at the other
// end via InARP.
//
// Note: AtmArpSearchForIPAddress addrefs pIpEntry.
//
CreateNewEntry = (pInterface->PVCOnly? FALSE: TRUE);
AA_ACQUIRE_IF_TABLE_LOCK(pInterface);
pIpEntry = AtmArpSearchForIPAddress(
pInterface,
&Destination,
IE_REFTYPE_TMP,
IsBroadcastAddress,
CreateNewEntry
);
AA_RELEASE_IF_TABLE_LOCK(pInterface);
if (pIpEntry == NULL_PATMARP_IP_ENTRY)
{
Status = NDIS_STATUS_RESOURCES;
break;
}
AA_ACQUIRE_IE_LOCK(pIpEntry);
AA_ASSERT(AA_IE_IS_ALIVE(pIpEntry));
if (pInterface->PVCOnly && (pIpEntry->pAtmEntry == NULL))
{
//
// This can happen if we had an active PVC and had learnt an IP address
// via InARP, and then the user had deleted the PVC. We would then be
// left with an IP entry, but no matching ATM entry. Abort this entry
// now.
//
AADEBUGP(AAD_FATAL,
("IfTransmit (PVC 2): IPEntry %x, Ref %d, Flags %x has NULL ATM Entry\n",
pIpEntry, pIpEntry->RefCount, pIpEntry->Flags));
rc = AA_DEREF_IE(pIpEntry, IE_REFTYPE_TMP);
if (rc != 0)
{
AtmArpAbortIPEntry(pIpEntry);
//
// IE Lock is released above.
//
}
Status = NDIS_STATUS_SUCCESS;
break;
}
//
// Keep a pointer to this IP Entry in the Route Cache Entry
// to speed things up for the next packet.
//
if (pRCE != (RouteCacheEntry *)NULL)
{
AtmArpLinkRCE(pRCE, pIpEntry);
}
//
// Note: AtmArpSerchForIPAddress addrefd pIpEntry for us -- we don't
// deref it right now because it could be a new entry! Instead,
// we deref it once we're done with it..
//
//
// Check if this IP Address has been resolved to an ATM address,
// and is "clean" (not aged out).
//
if (AA_IS_FLAG_SET(
pIpEntry->Flags,
AA_IP_ENTRY_STATE_MASK,
AA_IP_ENTRY_RESOLVED))
{
AA_ASSERT(pIpEntry->pAtmEntry != NULL_PATMARP_ATM_ENTRY);
pAtmEntry = pIpEntry->pAtmEntry;
AA_ACQUIRE_AE_LOCK_DPC(pAtmEntry);
AA_REF_AE(pAtmEntry, AE_REFTYPE_TMP);// Temp ref: IfTransmit
AA_RELEASE_AE_LOCK_DPC(pAtmEntry);
#ifdef IPMCAST
if (AA_IS_FLAG_SET(
pIpEntry->Flags,
AA_IP_ENTRY_MC_VALIDATE_MASK,
AA_IP_ENTRY_MC_REVALIDATE))
{
AA_SET_FLAG(pIpEntry->Flags,
AA_IP_ENTRY_MC_VALIDATE_MASK,
AA_IP_ENTRY_MC_REVALIDATING);
NeedMcRevalidation = TRUE;
}
#endif // IPMCAST
{
//
// AtmArpSearchForIPAddress addref'd pIpEntry for us, so
// before heading out of here, we deref it...
//
ULONG rc = AA_DEREF_IE(pIpEntry, IE_REFTYPE_TMP);
if (rc > 0)
{
AA_RELEASE_IE_LOCK(pIpEntry);
}
else
{
//
// It's gone ...
//
pIpEntry = NULL_PATMARP_IP_ENTRY;
AA_ASSERT(!NeedMcRevalidation);
NeedMcRevalidation = FALSE; // just to be safe.
}
}
AA_ACQUIRE_AE_LOCK(pAtmEntry);
Status = AtmArpSendPacketOnAtmEntry(
pInterface,
pAtmEntry,
pNdisPacket,
pFlowSpec,
pFilterSpec,
pFlowInfo,
IsBroadcastAddress
);
//
// The ATM Entry lock is released within the above. Get rid of the
// temp ref:
//
AA_ACQUIRE_AE_LOCK(pAtmEntry);
if (AA_DEREF_AE(pAtmEntry, AE_REFTYPE_TMP) != 0) // Temp ref: IfTransmit
{
AA_RELEASE_AE_LOCK(pAtmEntry);
}
break;
}
//
// We don't have the ATM address yet, but we have an
// IP Entry for the Destination IP address. Queue this
// packet on the IP Entry, and start Address resolution
// if not already started.
//
// SearchForIPAddress addrefd pIpEntry for us. We don't simply
// deref it here because it could be a brand new entry, with
// refcount == 1. So instead we simply decrement the refcount. Note
// that we do hold the lock on it at this time.
//
AA_ASSERT(pIpEntry->RefCount > 0);
AA_DEREF_IE_NO_DELETE(pIpEntry, IE_REFTYPE_TMP);
Status = AtmArpQueuePacketOnIPEntry(
pIpEntry,
pNdisPacket
);
//
// The IP Entry lock is released within the above.
//
break;
}
while (FALSE);
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
#ifdef IPMCAST
if (NeedMcRevalidation)
{
AAMCDEBUGP(AAD_LOUD,
("IfTransmit(MC): Revalidating pIpEntry 0x%x/0x%x, Addr %d.%d.%d.%d\n",
pIpEntry, pIpEntry->Flags,
((PUCHAR)&(pIpEntry->IPAddress))[0],
((PUCHAR)&(pIpEntry->IPAddress))[1],
((PUCHAR)&(pIpEntry->IPAddress))[2],
((PUCHAR)&(pIpEntry->IPAddress))[3]));
AtmArpMcSendRequest(
pInterface,
&Destination
);
}
#endif // IPMCAST
#ifdef PERF
if ((Status != NDIS_STATUS_SUCCESS) && (Status != NDIS_STATUS_PENDING))
{
AadLogSendAbort(pNdisPacket);
}
#endif // PERF
if (Status != NDIS_STATUS_PENDING)
{
Status = NDIS_STATUS_SUCCESS;
}
return (Status);
}
NDIS_STATUS
AtmArpIfTransfer(
IN PVOID Context,
IN NDIS_HANDLE Context1,
IN UINT ArpHdrOffset,
IN UINT ProtoOffset,
IN UINT BytesWanted,
IN PNDIS_PACKET pNdisPacket,
OUT PUINT pTransferCount
)
/*++
Routine Description:
This routine is called from the IP layer in order to copy in the
contents of a received packet that we indicated up earlier. The
context we had passed up in the receive indication is given back to
us, so that we can identify what it was that we passed up.
We simply call NDIS to do the transfer.
Arguments:
Context - Actually a pointer to our Interface structure
Context1 - Packet context we had passed up (pointer to NDIS packet)
ArpHdrOffset - Offset we had passed up in the receive indicate
ProtoOffset - The offset into higher layer protocol data to start copy from
BytesWanted - The amount of data to be copied
pNdisPacket - The packet to be copied into
pTransferCount - Where we return the actual #bytes copied
Return Value:
NDIS_STATUS_SUCCESS always.
--*/
{
AADEBUGP(AAD_EXTRA_LOUD,
("IfTransfer: Ctx 0x%x, Ctx1 0x%x, HdrOff %d, ProtOff %d, Wanted %d, Pkt 0x%x\n",
Context,
Context1,
ArpHdrOffset,
ProtoOffset,
BytesWanted,
pNdisPacket));
NdisCopyFromPacketToPacket(
pNdisPacket,
0,
BytesWanted,
(PNDIS_PACKET)Context1,
ArpHdrOffset+ProtoOffset,
pTransferCount
);
return (NDIS_STATUS_SUCCESS);
}
VOID
AtmArpIfInvalidate(
IN PVOID Context,
IN RouteCacheEntry * pRCE
)
/*++
Routine Description:
This routine is called from the IP layer to invalidate a Route Cache
Entry. If this RCE is associated with one of our IP Entries, unlink
it from the list of RCE's pointing to that IP entry.
Arguments:
Context - Actually a pointer to our Interface structure
pRCE - Pointer to Route Cache Entry being invalidated.
Return Value:
None
--*/
{
PATMARP_INTERFACE pInterface;
PATMARP_IP_ENTRY pIpEntry;
PATMARP_RCE_CONTEXT pRCEContext;
ULONG rc; // Ref Count for IP Entry
#if DBG
AA_IRQL EntryIrq, ExitIrq;
#endif
AA_GET_ENTRY_IRQL(EntryIrq);
AA_ASSERT(pRCE != (RouteCacheEntry *)NULL);
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
AA_ACQUIRE_IF_TABLE_LOCK(pInterface);
pRCEContext = (PATMARP_RCE_CONTEXT)(&(pRCE->rce_context[0]));
//
// Get the ATMARP IP Entry associated with this RCE.
//
pIpEntry = (PATMARP_IP_ENTRY)pRCEContext->pIpEntry;
if (pIpEntry != NULL_PATMARP_IP_ENTRY)
{
AADEBUGP(AAD_LOUD, ("IfInvalidate: pIf 0x%x, pRCE 0x%x, pIpEntry 0x%x\n",
pInterface, pRCE, pIpEntry));
AA_ACQUIRE_IE_LOCK_DPC(pIpEntry);
if (AtmArpUnlinkRCE(pRCE, pIpEntry))
{
rc = AA_DEREF_IE(pIpEntry, IE_REFTYPE_RCE); // RCE
if (rc > 0)
{
AA_RELEASE_IE_LOCK_DPC(pIpEntry);
}
//
// else the IP Entry is gone.
//
}
else
{
AA_RELEASE_IE_LOCK_DPC(pIpEntry);
}
}
AA_SET_MEM((PUCHAR)(&(pRCE->rce_context[0])), 0, RCE_CONTEXT_SIZE);
AA_RELEASE_IF_TABLE_LOCK(pInterface);
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
return;
}
BOOLEAN
AtmArpUnlinkRCE(
IN RouteCacheEntry * pRCE,
IN PATMARP_IP_ENTRY pIpEntry
)
/*++
Routine Description:
Unlink an RCE from the list of RCE's associated with an IP Entry.
It is assumed that the caller holds locks to the IF Table and
to the IP Entry.
Arguments:
pRCE - RCE to be unlinked.
pIpEntry - ATMARP IP Entry from which the RCE is to be
removed.
Return Value:
TRUE if the RCE was indeed in the list for the IP Entry, FALSE
otherwise.
--*/
{
BOOLEAN Found; // Did we find the RCE?
RouteCacheEntry ** ppRCE; // Used for walking the list of RCEs
PATMARP_RCE_CONTEXT pRCEContext;
//
// Initialize
//
Found = FALSE;
//
// Go down the list of RCEs attached to this IP Entry, and
// find this RCE's position. We remember a pointer to the
// place that keeps the address of this RCE (i.e. ppRCE),
// so that we can remove this RCE from the list quickly.
//
ppRCE = &(pIpEntry->pRCEList);
while (*ppRCE != pRCE)
{
pRCEContext = (PATMARP_RCE_CONTEXT)(&((*ppRCE)->rce_context[0]));
if (pRCEContext->pNextRCE == (RouteCacheEntry *)NULL)
{
//
// Allow for the RCE to be absent in the list?
//
AA_ASSERT(FALSE); // REMOVELATER
break;
}
else
{
//
// Walk down the list.
//
ppRCE = &(pRCEContext->pNextRCE);
}
}
if (*ppRCE == pRCE)
{
//
// We found it. Make the predecessor point to the successor.
//
pRCEContext = (PATMARP_RCE_CONTEXT)(&(pRCE->rce_context[0]));
*ppRCE = pRCEContext->pNextRCE;
pRCEContext->pIpEntry = NULL_PATMARP_IP_ENTRY;
Found = TRUE;
}
return (Found);
}
VOID
AtmArpLinkRCE(
IN RouteCacheEntry * pRCE,
IN PATMARP_IP_ENTRY pIpEntry LOCKIN LOCKOUT
)
/*++
Routine Description:
Link an RCE to an IP Entry's list of RCEs. Check if the RCE is already
present - if so, ignore this.
The caller is assumed to hold a lock to the IP Entry.
Arguments:
pRCE - RCE to be linked.
pIpEntry - ATMARP IP Entry to which the RCE is to be
linked.
Return Value:
None
--*/
{
RouteCacheEntry ** ppRCE; // Used for walking the list of RCEs
PATMARP_RCE_CONTEXT pRCEContext;
ppRCE = &(pIpEntry->pRCEList);
//
// Check if the RCE is already present.
//
while (*ppRCE != NULL)
{
if (*ppRCE == pRCE)
{
//
// Found it.
//
break;
}
//
// Move to the next.
//
pRCEContext = (PATMARP_RCE_CONTEXT)(&((*ppRCE)->rce_context[0]));
ppRCE = &(pRCEContext->pNextRCE);
}
if (*ppRCE == NULL)
{
//
// This RCE is not present in the IP Entry's list. Add it.
//
pRCEContext = (PATMARP_RCE_CONTEXT)&(pRCE->rce_context[0]);
pRCEContext->pIpEntry = pIpEntry;
pRCEContext->pNextRCE = pIpEntry->pRCEList;
pIpEntry->pRCEList = pRCE;
AA_REF_IE(pIpEntry, IE_REFTYPE_RCE); // RCE ref
}
else
{
AADEBUGP(AAD_LOUD, ("AtmArpLinkRCE: RCE 0x%x already linked to IP Entry 0x%x\n",
pRCE, pIpEntry));
}
}
INT
AtmArpIfQueryInfo(
IN PVOID Context,
IN TDIObjectID * pID,
IN PNDIS_BUFFER pNdisBuffer,
IN OUT PUINT pBufferSize,
IN PVOID QueryContext
)
/*++
Routine Description:
This is called from the IP layer to query for statistics or other
information about an interface.
Arguments:
Context - Actually a pointer to our ATMARP Interface
pID - Describes the object being queried
pNdisBuffer - Space for returning information
pBufferSize - Pointer to size of above. On return, we fill
it with the actual bytes copied.
QueryContext - Context value pertaining to the query.
Return Value:
TDI Status code.
--*/
{
PATMARP_INTERFACE pInterface;
UINT EntityType;
UINT Instance;
UINT BufferSize;
UINT ByteOffset;
UINT BytesCopied;
INT ReturnStatus;
BOOLEAN DataLeft;
BOOLEAN ContextValid;
UCHAR InfoBuff[sizeof(IFEntry)]; // Temp space for return value
#if DBG
AA_IRQL EntryIrq, ExitIrq;
ULONG OldDebugLevel;
#endif
AA_GET_ENTRY_IRQL(EntryIrq);
EntityType = pID->toi_entity.tei_entity;
Instance = pID->toi_entity.tei_instance;
BufferSize = *pBufferSize;
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
#if DBG
OldDebugLevel = AaDebugLevel;
#endif
AADEBUGP(AAD_LOUD,
("IfQueryInfo: pIf 0x%x, pID 0x%x, pBuf 0x%x, Size %d, Ent %d, Inst %d\n",
pInterface, pID, pNdisBuffer, BufferSize, EntityType, Instance));
//
// Initialize
//
ByteOffset = 0;
ReturnStatus = TDI_INVALID_PARAMETER;
do
{
if (pInterface->AdminState == IF_STATUS_DOWN)
{
ReturnStatus = TDI_INVALID_REQUEST;
break;
}
//
// Check the Entity and Instance values.
//
if ((EntityType != AT_ENTITY || Instance != pInterface->ATInstance) &&
(EntityType != IF_ENTITY || Instance != pInterface->IFInstance))
{
AADEBUGP(AAD_VERY_LOUD,
("Mismatch: Entity %d, AT_ENTITY %d, Inst %d, IF AT Inst %d, IF_ENTITY %d, IF IF Inst %d\n",
EntityType,
AT_ENTITY,
Instance,
pInterface->ATInstance,
IF_ENTITY,
pInterface->IFInstance
));
#if DBG_QRY
if (!AaIgnoreInstance)
{
ReturnStatus = TDI_INVALID_REQUEST;
break;
}
#else
#ifndef ATMARP_WIN98
ReturnStatus = TDI_INVALID_REQUEST;
break;
#endif // !ATMARP_WIN98
#endif // DBG_QRY
}
AADEBUGP(AAD_LOUD, ("QueryInfo: pID 0x%x, toi_type %d, toi_class %d, toi_id %d\n",
pID, pID->toi_type, pID->toi_class, pID->toi_id));
*pBufferSize = 0;
if (pID->toi_type != INFO_TYPE_PROVIDER)
{
AADEBUGP(AAD_VERY_LOUD, ("toi_type %d != PROVIDER (%d)\n",
pID->toi_type,
INFO_TYPE_PROVIDER));
ReturnStatus = TDI_INVALID_PARAMETER;
break;
}
if (pID->toi_class == INFO_CLASS_GENERIC)
{
if (pID->toi_id == ENTITY_TYPE_ID)
{
if (BufferSize >= sizeof(UINT))
{
AADEBUGP(AAD_VERY_LOUD,
("INFO GENERIC, ENTITY TYPE, BufferSize %d\n", BufferSize));
*((PUINT)&(InfoBuff[0])) = ((EntityType == AT_ENTITY) ? AT_ARP: IF_MIB);
if (AtmArpCopyToNdisBuffer(
pNdisBuffer,
InfoBuff,
sizeof(UINT),
&ByteOffset) != NULL)
{
// *pBufferSize = sizeof(UINT);
ReturnStatus = TDI_SUCCESS;
}
else
{
ReturnStatus = TDI_NO_RESOURCES;
}
}
else
{
ReturnStatus = TDI_BUFFER_TOO_SMALL;
}
}
else
{
ReturnStatus = TDI_INVALID_PARAMETER;
}
break;
}
if (EntityType == AT_ENTITY)
{
//
// This query is for an Address Translation Object.
//
if (pID->toi_id == AT_MIB_ADDRXLAT_INFO_ID)
{
//
// Request for the number of entries in the address translation
// table, and the IF index.
//
AddrXlatInfo *pAXI;
AADEBUGP(AAD_VERY_LOUD, ("QueryInfo: AT Entity, for IF index, ATE size\n"));
if (BufferSize >= sizeof(AddrXlatInfo))
{
*pBufferSize = sizeof(AddrXlatInfo);
pAXI = (AddrXlatInfo *)InfoBuff;
pAXI->axi_count = pInterface->NumOfArpEntries;
pAXI->axi_index = pInterface->IFIndex;
if (AtmArpCopyToNdisBuffer(
pNdisBuffer,
InfoBuff,
sizeof(AddrXlatInfo),
&ByteOffset) != NULL)
{
ReturnStatus = TDI_SUCCESS;
}
else
{
ReturnStatus = TDI_NO_RESOURCES;
}
}
else
{
ReturnStatus = TDI_BUFFER_TOO_SMALL;
}
break;
}
if (pID->toi_id == AT_MIB_ADDRXLAT_ENTRY_ID)
{
//
// Request for reading the address translation table.
//
AADEBUGP(AAD_VERY_LOUD, ("QueryInfo: AT Entity, for reading ATE\n"));
AA_ACQUIRE_IF_TABLE_LOCK(pInterface);
DataLeft = AtmArpValidateTableContext(
QueryContext,
pInterface,
&ContextValid
);
if (!ContextValid)
{
AA_RELEASE_IF_TABLE_LOCK(pInterface);
ReturnStatus = TDI_INVALID_PARAMETER;
break;
}
BytesCopied = 0;
ReturnStatus = TDI_SUCCESS;
while (DataLeft)
{
if ((INT)BufferSize - (INT)BytesCopied >=
sizeof(IPNetToMediaEntry))
{
//
// Space left in output buffer.
//
DataLeft = AtmArpReadNextTableEntry(
QueryContext,
pInterface,
InfoBuff
);
BytesCopied += sizeof(IPNetToMediaEntry);
pNdisBuffer = AtmArpCopyToNdisBuffer(
pNdisBuffer,
InfoBuff,
sizeof(IPNetToMediaEntry),
&ByteOffset
);
if (pNdisBuffer == NULL)
{
BytesCopied = 0;
ReturnStatus = TDI_NO_RESOURCES;
break;
}
}
else
{
break;
}
}
AA_RELEASE_IF_TABLE_LOCK(pInterface);
*pBufferSize = BytesCopied;
if (ReturnStatus == TDI_SUCCESS)
{
ReturnStatus = (!DataLeft? TDI_SUCCESS : TDI_BUFFER_OVERFLOW);
}
break;
}
ReturnStatus = TDI_INVALID_PARAMETER;
break;
}
if (pID->toi_class != INFO_CLASS_PROTOCOL)
{
ReturnStatus = TDI_INVALID_PARAMETER;
break;
}
if (pID->toi_id == IF_MIB_STATS_ID)
{
//
// Request for Interface level statistics.
//
IFEntry *pIFEntry = (IFEntry *)InfoBuff;
AADEBUGP(AAD_VERY_LOUD, ("QueryInfo: MIB statistics\n"));
//
// Check if we have enough space.
//
if (BufferSize < IFE_FIXED_SIZE)
{
ReturnStatus = TDI_BUFFER_TOO_SMALL;
break;
}
pIFEntry->if_index = pInterface->IFIndex;
pIFEntry->if_mtu = pInterface->MTU;
pIFEntry->if_type = IF_TYPE_OTHER;
pIFEntry->if_speed = pInterface->Speed;
pIFEntry->if_adminstatus = pInterface->AdminState;
if (pInterface->State == IF_STATUS_UP)
{
pIFEntry->if_operstatus = IF_OPER_STATUS_OPERATIONAL;
}
else
{
pIFEntry->if_operstatus = IF_OPER_STATUS_NON_OPERATIONAL;
}
pIFEntry->if_lastchange = pInterface->LastChangeTime;
pIFEntry->if_inoctets = pInterface->InOctets;
pIFEntry->if_inucastpkts = pInterface->InUnicastPkts;
pIFEntry->if_innucastpkts = pInterface->InNonUnicastPkts;
pIFEntry->if_indiscards = pInterface->InDiscards;
pIFEntry->if_inerrors = pInterface->InErrors;
pIFEntry->if_inunknownprotos = pInterface->UnknownProtos;
pIFEntry->if_outoctets = pInterface->OutOctets;
pIFEntry->if_outucastpkts = pInterface->OutUnicastPkts;
pIFEntry->if_outnucastpkts = pInterface->OutNonUnicastPkts;
pIFEntry->if_outdiscards = pInterface->OutDiscards;
pIFEntry->if_outerrors = pInterface->OutErrors;
pIFEntry->if_outqlen = 0;
pIFEntry->if_descrlen = pInterface->pAdapter->DescrLength;
#ifndef ATMARP_WIN98
pIFEntry->if_physaddrlen = AA_ATM_PHYSADDR_LEN;
AA_COPY_MEM(
pIFEntry->if_physaddr,
&(pInterface->pAdapter->MacAddress[0]),
AA_ATM_ESI_LEN
);
pIFEntry->if_physaddr[AA_ATM_PHYSADDR_LEN-1] = (UCHAR)pInterface->SapSelector;
#else
//
// Win98: winipcfg doesn't like 7 byte long physical address.
//
pIFEntry->if_physaddrlen = AA_ATM_ESI_LEN;
AA_COPY_MEM(
pIFEntry->if_physaddr,
&(pInterface->pAdapter->MacAddress[0]),
AA_ATM_ESI_LEN
);
//
// Since w're only reporting 6 bytes, we need to make the reported
// MAC address look different from what LANE reports (LANE reports
// the MAC address). So we simply put the special value 0x0a 0xac
// (for aac, or "atm arp client") in the 1st USHORTS.
//
pIFEntry->if_physaddr[0] = 0x0a;
pIFEntry->if_physaddr[1] = 0xac;
#endif
if (AtmArpCopyToNdisBuffer(
pNdisBuffer,
InfoBuff,
IFE_FIXED_SIZE,
&ByteOffset) == NULL)
{
*pBufferSize = 0;
ReturnStatus = TDI_NO_RESOURCES;
break;
}
if (BufferSize >= (IFE_FIXED_SIZE + pIFEntry->if_descrlen))
{
*pBufferSize = IFE_FIXED_SIZE + pIFEntry->if_descrlen;
ReturnStatus = TDI_SUCCESS;
if (pIFEntry->if_descrlen != 0)
{
if (AtmArpCopyToNdisBuffer(
pNdisBuffer,
pInterface->pAdapter->pDescrString,
pIFEntry->if_descrlen,
&ByteOffset) == NULL)
{
// Failed to copy descr string
*pBufferSize = IFE_FIXED_SIZE;
ReturnStatus = TDI_NO_RESOURCES;
}
}
}
else
{
*pBufferSize = IFE_FIXED_SIZE;
ReturnStatus = TDI_BUFFER_OVERFLOW;
}
break;
}
}
while (FALSE);
AA_CHECK_EXIT_IRQL(EntryIrq, ExitIrq);
AADEBUGP(AAD_LOUD, ("QueryInfo: returning 0x%x (%s), BufferSize %d\n",
ReturnStatus,
((ReturnStatus == TDI_SUCCESS)? "SUCCESS": "FAILURE"),
*pBufferSize
));
#if DBG
AaDebugLevel = OldDebugLevel;
#endif
return (ReturnStatus);
}
INT
AtmArpIfSetInfo(
IN PVOID Context,
IN TDIObjectID * pID,
IN PVOID pBuffer,
IN UINT BufferSize
)
/*++
Routine Description:
This is called from the IP layer to set the value of an object
for an interface.
Arguments:
Context - Actually a pointer to our ATMARP Interface
pID - Describes the object being set
pBuffer - Value for the object
BufferSize - Size of above
Return Value:
TDI Status code.
--*/
{
AADEBUGP(AAD_ERROR, ("IfSetInfo: pIf 0x%x, Will return failure!\n",
Context));
return (TDI_INVALID_REQUEST); // TBD: support Sets.
}
INT
AtmArpIfGetEList(
IN PVOID Context,
IN TDIEntityID * pEntityList,
IN OUT PUINT pEntityListSize
)
/*++
Routine Description:
This routine is called when the interface starts up, in order to
assign all relevant Entity Instance numbers for an interface.
The ATMARP module belongs to the "AT" and "IF" types. The entity
list is a list of <Entity type, Instance number> tuples that have
been filled in by other modules.
For each of the entity types we support, we find the largest
instance number in use (by walking thru the Entity list), and
assign to ourselves the next larger number in each case. Using
these numbers, we append our tuples to the end of the Entity list,
if there is enough space.
NT 5: we may find that our entries are already present, in which
case we don't create new entries.
It is assumed that this isn't reentered. If this assumption is
false, we should acquire our Interface lock in here.
Arguments:
Context - Actually a pointer to our ATMARP Interface
pEntityList - Pointer to TDI Entity list
pEntityListSize - Pointer to length of above list. We update
this if we add our entries to the list.
Return Value:
TRUE if successful, FALSE otherwise.
--*/
{
PATMARP_INTERFACE pInterface;
UINT EntityCount; // Total elements in Entity list
UINT i; // Iteration counter
UINT MyATInstance; // "AT" Instance number we assign to ourselves
UINT MyIFInstance; // "IF" Instance number we assign to ourselves
INT ReturnValue;
TDIEntityID * pATEntity; // Points to our AT entry
TDIEntityID * pIFEntity; // Points to our IF entry
pInterface = (PATMARP_INTERFACE)Context;
AA_STRUCT_ASSERT(pInterface, aai);
EntityCount = *pEntityListSize;
pATEntity = NULL;
pIFEntity = NULL;
MyATInstance = MyIFInstance = 0;
AADEBUGP(AAD_INFO, ("IfGetEList: pIf 0x%x, pList 0x%x, Cnt %d\n",
pInterface, pEntityList, EntityCount));
do
{
#ifdef OLD_ENTITY_LIST
//
// We need space for 2 entries; see if this is available.
//
if (EntityCount + 2 > MAX_TDI_ENTITIES)
{
ReturnValue = FALSE;
break;
}
//
// Search for the max used-up instance numbers for the "AT"
// and "IF" types.
//
MyATInstance = MyIFInstance = 0;
for (i = 0; i < EntityCount; i++, pEntityList++)
{
if (pEntityList->tei_entity == AT_ENTITY)
{
MyATInstance = MAX(MyATInstance, pEntityList->tei_instance + 1);
}
else if (pEntityList->tei_entity == IF_ENTITY)
{
MyIFInstance = MAX(MyIFInstance, pEntityList->tei_instance + 1);
}
}
//
// Save our instance numbers for later use.
//
pInterface->ATInstance = MyATInstance;
pInterface->IFInstance = MyIFInstance;
//
// Append our AT and IF entries to the Entity list.
// Recall that we just traversed the list fully, so we
// are pointing to the right place to add entries.
//
pEntityList->tei_entity = AT_ENTITY;
pEntityList->tei_instance = MyATInstance;
pEntityList++;
pEntityList->tei_entity = IF_ENTITY;
pEntityList->tei_instance = MyIFInstance;
//
// Return the new list size.
//
*pEntityListSize += 2;
ReturnValue = TRUE;
#else
//
// Walk down the list, looking for AT/IF entries matching our
// instance values. Also remember the largest AT and IF instance
// values we see, so that we can allocate the next larger values
// for ourselves, in case we don't have instance values assigned.
//
for (i = 0; i < EntityCount; i++, pEntityList++)
{
//
// Skip invalid entries.
//
if (pEntityList->tei_instance == INVALID_ENTITY_INSTANCE)
{
continue;
}
if (pEntityList->tei_entity == AT_ENTITY)
{
if (pEntityList->tei_instance == pInterface->ATInstance)
{
//
// This is our AT entry.
//
pATEntity = pEntityList;
}
else
{
MyATInstance = MAX(MyATInstance, pEntityList->tei_instance + 1);
}
}
else if (pEntityList->tei_entity == IF_ENTITY)
{
if (pEntityList->tei_instance == pInterface->IFInstance)
{
//
// This is our IF entry.
//
pIFEntity = pEntityList;
}
else
{
MyIFInstance = MAX(MyIFInstance, pEntityList->tei_instance + 1);
}
}
}
ReturnValue = TRUE;
//
// Update or create our Address Translation entry.
//
if (pATEntity)
{
//
// We found our entry.
//
if (pInterface->AdminState == IF_STATUS_DOWN)
{
pATEntity->tei_instance = INVALID_ENTITY_INSTANCE;
}
}
else
{
//
// Grab an entry for ourselves, unless we are shutting down.
//
if (pInterface->AdminState == IF_STATUS_DOWN)
{
break;
}
if (EntityCount >= MAX_TDI_ENTITIES)
{
ReturnValue = FALSE;
break;
}
pEntityList->tei_entity = AT_ENTITY;
pEntityList->tei_instance = MyATInstance;
pInterface->ATInstance = MyATInstance;
pEntityList++;
(*pEntityListSize)++;
EntityCount++;
}
//
// Update or create or IF entry.
//
if (pIFEntity)
{
//
// We found our entry.
//
if (pInterface->AdminState == IF_STATUS_DOWN)
{
pIFEntity->tei_instance = INVALID_ENTITY_INSTANCE;
}
}
else
{
//
// Grab an entry for ourselves, unless we are shutting down.
//
if (pInterface->AdminState == IF_STATUS_DOWN)
{
break;
}
if (EntityCount >= MAX_TDI_ENTITIES)
{
ReturnValue = FALSE;
break;
}
pEntityList->tei_entity = IF_ENTITY;
pEntityList->tei_instance = MyIFInstance;
pInterface->IFInstance = MyIFInstance;
pEntityList++;
(*pEntityListSize)++;
EntityCount++;
}
#endif // OLD_ENTITY_LIST
}
while (FALSE);
AADEBUGP(AAD_INFO,
("IfGetEList: returning %d, MyAT %d, MyIF %d, pList 0x%x, Size %d\n",
ReturnValue, MyATInstance, MyIFInstance, pEntityList, *pEntityListSize));
return (ReturnValue);
}
#ifdef _PNP_POWER_
VOID
AtmArpIfPnPComplete(
IN PVOID Context,
IN NDIS_STATUS Status,
IN PNET_PNP_EVENT pNetPnPEvent
)
/*++
Routine Description:
This routine is called by IP when it completes a previous call
we made to its PnP event handler.
If this is the last Interface on the adapter, we complete the
NDIS PNP notification that lead to this. Otherwise, we indicate
this same event to IP on the next Interface on the adapter.
Arguments:
Context - Actually a pointer to our ATMARP Interface
Status - Completion status from IP
pNetPnPEvent - The PNP event
Return Value:
None
--*/
{
PATMARP_INTERFACE pInterface;
pInterface = (PATMARP_INTERFACE)Context;
AADEBUGP(AAD_INFO,
("IfPnPComplete: IF 0x%x, Status 0x%x, Event 0x%x\n",
pInterface, Status, pNetPnPEvent));
if (pInterface != NULL_PATMARP_INTERFACE)
{
AA_STRUCT_ASSERT(pInterface, aai);
if (pInterface->pNextInterface == NULL_PATMARP_INTERFACE)
{
NdisCompletePnPEvent(
Status,
pInterface->pAdapter->NdisAdapterHandle,
pNetPnPEvent
);
}
else
{
pInterface = pInterface->pNextInterface;
(*pInterface->IPPnPEventHandler)(
pInterface->IPContext,
pNetPnPEvent
);
}
}
else
{
NdisCompletePnPEvent(
Status,
NULL,
pNetPnPEvent
);
}
return;
}
#endif // _PNP_POWER_
#ifdef PROMIS
EXTERN
NDIS_STATUS
AtmArpIfSetNdisRequest(
IN PVOID Context,
IN NDIS_OID Oid,
IN UINT On
)
/*++
Routine Description:
ARP Ndisrequest handler.
Called by the upper driver to set the packet filter for the interface.
Arguments:
Context - Actually a pointer to our ATMARP Interface
OID - Object ID to set/unset
On - Set_if, clear_if or clear_card
Return Value:
Status
--*/
{
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
PATMARP_INTERFACE pInterface = (PATMARP_INTERFACE)Context;
AADEBUGP(AAD_INFO,("IfSetNdisRequest: pIF =0x%x; Oid=0x%x; On=%u\n",
pInterface,
Oid,
On
));
do
{
//
// We set IPAddress and mask to span the entire mcast address range...
//
IP_ADDRESS IPAddress = IP_CLASSD_MIN;
IP_MASK Mask = IP_CLASSD_MASK;
UINT ReturnStatus = TRUE;
NDIS_OID PrevOidValue;
if (Oid != NDIS_PACKET_TYPE_ALL_MULTICAST)
{
Status = NDIS_STATUS_NOT_SUPPORTED;
break;
}
AA_STRUCT_ASSERT(pInterface, aai);
AA_ACQUIRE_IF_LOCK(pInterface);
PrevOidValue = pInterface->EnabledIPFilters & NDIS_PACKET_TYPE_ALL_MULTICAST;
if (On)
{
if (PrevOidValue == 0)
{
pInterface->EnabledIPFilters |= NDIS_PACKET_TYPE_ALL_MULTICAST;
ReturnStatus = AtmArpMcAddAddress(pInterface, IPAddress, Mask);
//
// IF lock released above
//
}
else
{
AA_RELEASE_IF_LOCK(pInterface);
}
}
else
{
if (PrevOidValue != 0)
{
pInterface->EnabledIPFilters &= ~NDIS_PACKET_TYPE_ALL_MULTICAST;
ReturnStatus = AtmArpMcDelAddress(pInterface, IPAddress, Mask);
//
// IF lock released above
//
}
else
{
AA_RELEASE_IF_LOCK(pInterface);
}
}
if (ReturnStatus != TRUE)
{
//
// We've got to restore EnabledIPFilters to it's original value.
//
AA_ACQUIRE_IF_LOCK(pInterface);
pInterface->EnabledIPFilters &= ~NDIS_PACKET_TYPE_ALL_MULTICAST;
pInterface->EnabledIPFilters |= PrevOidValue;
AA_RELEASE_IF_LOCK(pInterface);
Status = NDIS_STATUS_FAILURE;
}
}
while (FALSE);
AADEBUGP(AAD_INFO, ("IfSetNdisRequest(pIF =0x%x) returns 0x%x\n",
pInterface,
Status
));
return Status;
}
#endif // PROMIS
PNDIS_BUFFER AtmArpFreeingBuffer = NULL;
PNDIS_PACKET AtmArpFreeingPacket = NULL;
AA_HEADER_TYPE AtmArpFreeingHdrType = 0;
VOID
AtmArpFreeSendPackets(
IN PATMARP_INTERFACE pInterface,
IN PNDIS_PACKET PacketList,
IN BOOLEAN HdrPresent
)
/*++
Routine Description:
Free a list of packets that were queued to be sent, but have been
"aborted". Each packet in this list is one of the following types:
(a) Belonging to IP (b) Belonging to the ATMARP module. In the case
of an IP packet, HdrPresent tells us whether or not we had prepended
an LLC/SNAP header to this packet, and its type: we need this information
because we need to reclaim such headers.
Also, in the case of IP packets, we call IP's Transmit Complete up-call,
to inform IP of a failed transmission.
Arguments:
pInterface - Pointer to ATMARP Interface on which these
packets would have been sent.
PacketList - Pointer to first packet in a list.
HdrPresent - Is an LLC/SNAP header present
Return Value:
None
--*/
{
PNDIS_PACKET pNdisPacket;
PNDIS_PACKET pNextPacket;
PNDIS_BUFFER pNdisBuffer;
ULONG NumberOfDiscards;
PacketContext *PC;
AA_HEADER_TYPE HdrType;
NumberOfDiscards = 0;
pNdisPacket = PacketList;
while (pNdisPacket != (PNDIS_PACKET)NULL)
{
NumberOfDiscards++;
pNextPacket = AA_GET_NEXT_PACKET(pNdisPacket);
AA_SET_NEXT_PACKET(pNdisPacket, NULL);
PC = (PacketContext *)pNdisPacket->ProtocolReserved;
if (PC->pc_common.pc_owner != PACKET_OWNER_LINK)
{
//
// Belongs to IP.
//
if (HdrPresent)
{
PUCHAR pData;
UINT Length;
#ifdef BACK_FILL
NdisQueryPacket(pNdisPacket, NULL, NULL, &pNdisBuffer, NULL);
AA_ASSERT(pNdisBuffer != NULL);
NdisQueryBuffer(pNdisBuffer, &pData, &Length);
if (pData[5] == LLC_SNAP_OUI2)
{
HdrType = AA_HEADER_TYPE_UNICAST;
}
else
{
HdrType = AA_HEADER_TYPE_NUNICAST;
}
//
// Now check if we had attached a header buffer or not.
//
if (AtmArpDoBackFill && AA_BACK_FILL_POSSIBLE(pNdisBuffer))
{
ULONG HeaderLength;
AADEBUGP(AAD_VERY_LOUD,
("FreeSendPackets: IF %x, Pkt %x Buf %x has been backfilled\n",
pInterface, pNdisPacket, pNdisBuffer));
//
// We would have back-filled IP's buffer with the LLC/SNAP
// header. Remove the back-fill.
//
HeaderLength = ((HdrType == AA_HEADER_TYPE_UNICAST)?
sizeof(AtmArpLlcSnapHeader) :
#ifdef IPMCAST
sizeof(AtmArpMcType1ShortHeader));
#else
0);
#endif // IPMCAST
(PUCHAR)pNdisBuffer->MappedSystemVa += HeaderLength;
pNdisBuffer->ByteOffset += HeaderLength;
pNdisBuffer->ByteCount -= HeaderLength;
}
else
{
//
// The first buffer would be our header buffer. Remove
// it from the packet and return to our pool.
//
NdisUnchainBufferAtFront(pNdisPacket, &pNdisBuffer);
AtmArpFreeingBuffer = pNdisBuffer; // to help debugging
AtmArpFreeingPacket = pNdisPacket; // to help debugging
AtmArpFreeingHdrType = HdrType;
AtmArpFreeHeader(pInterface, pNdisBuffer, HdrType);
}
#else // BACK_FILL
//
// Free the LLC/SNAP header buffer.
//
NdisUnchainBufferAtFront(pNdisPacket, &pNdisBuffer);
AA_ASSERT(pNdisBuffer != NULL);
NdisQueryBuffer(pNdisBuffer, &pData, &Length);
if (pData[5] == LLC_SNAP_OUI2)
{
HdrType = AA_HEADER_TYPE_UNICAST;
}
else
{
AA_ASSERT(pData[5] == MC_LLC_SNAP_OUI2);
HdrType = AA_HEADER_TYPE_NUNICAST;
}
AtmArpFreeHeader(pInterface, pNdisBuffer, HdrType);
#endif // BACK_FILL
}
//
// Inform IP of send completion.
//
(*(pInterface->IPTxCmpltHandler))(
pInterface->IPContext,
pNdisPacket,
NDIS_STATUS_FAILURE
);
}
else
{
//
// Belongs to us.
//
NdisUnchainBufferAtFront(pNdisPacket, &pNdisBuffer);
AtmArpFreeProtoBuffer(pInterface, pNdisBuffer);
AtmArpFreePacket(pInterface, pNdisPacket);
}
//
// Go to next packet in the list.
//
pNdisPacket = pNextPacket;
}
//
// Update IF statistics
//
AA_IF_STAT_ADD(pInterface, OutDiscards, NumberOfDiscards);
}
#define IPNetMask(a) AtmArpIPMaskTable[(*(uchar *)&(a)) >> 4]
BOOLEAN
AtmArpIsBroadcastIPAddress(
IN IP_ADDRESS Addr,
IN PATMARP_INTERFACE pInterface LOCKIN LOCKOUT
)
/*++
Routine Description:
Check if the given IP address is a broadcast address for the
interface.
Copied from the LAN ARP module.
Arguments:
Addr - The IP Address to be checked
pInterface - Pointer to our Interface structure
Return Value:
TRUE if the address is a broadcast address, FALSE otherwise.
--*/
{
IP_ADDRESS BCast;
IP_MASK Mask;
PIP_ADDRESS_ENTRY pIpAddressEntry;
IP_ADDRESS LocalAddr;
// First get the interface broadcast address.
BCast = pInterface->BroadcastAddress;
// First check for global broadcast.
if (IP_ADDR_EQUAL(BCast, Addr) || CLASSD_ADDR(Addr))
return TRUE;
// Now walk the local addresses, and check for net/subnet bcast on each
// one.
pIpAddressEntry = &(pInterface->LocalIPAddress);
do {
// See if this one is valid.
LocalAddr = pIpAddressEntry->IPAddress;
if (!IP_ADDR_EQUAL(LocalAddr, NULL_IP_ADDR)) {
// He's valid.
Mask = pIpAddressEntry->IPMask;
// First check for subnet bcast.
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
return TRUE;
// Now check all nets broadcast.
Mask = IPNetMask(LocalAddr);
if (IP_ADDR_EQUAL((LocalAddr & Mask) | (BCast & ~Mask), Addr))
return TRUE;
}
pIpAddressEntry = pIpAddressEntry->pNext;
} while (pIpAddressEntry != NULL);
// If we're here, it's not a broadcast.
return FALSE;
}
BOOLEAN
AtmArpValidateTableContext(
IN PVOID QueryContext,
IN PATMARP_INTERFACE pInterface,
IN BOOLEAN * pIsValid
)
/*++
Routine Description:
Check if a given ARP Table Query context is valid. It is valid if it
is either NULL (looking for the first entry) or indicates a valid
ARP Table Entry.
Arguments:
QueryContext - The context to be validated
pInterface - The IF on which the query is being performed
pIsValid - Where we return the validity of the Query Context.
Return Value:
TRUE if the ARP Table has data to be read beyond the given context,
FALSE otherwise.
--*/
{
IPNMEContext *pNMContext = (IPNMEContext *)QueryContext;
PATMARP_IP_ENTRY pIpEntry;
PATMARP_IP_ENTRY pTargetIpEntry;
UINT i;
BOOLEAN ReturnValue;
i = pNMContext->inc_index;
pTargetIpEntry = (PATMARP_IP_ENTRY)(pNMContext->inc_entry);
//
// Check if we are starting at the beginning of the ARP Table.
//
if ((i == 0) && (pTargetIpEntry == NULL_PATMARP_IP_ENTRY))
{
//
// Yes, we are. Find the very first entry in the hash table.
//
*pIsValid = TRUE;
do
{
if ((pIpEntry = pInterface->pArpTable[i]) != NULL_PATMARP_IP_ENTRY)
{
break;
}
i++;
}
while (i < ATMARP_TABLE_SIZE);
if (pIpEntry != NULL_PATMARP_IP_ENTRY)
{
pNMContext->inc_index = i;
pNMContext->inc_entry = pIpEntry;
ReturnValue = TRUE;
}
else
{
ReturnValue = FALSE;
}
}
else
{
//
// We are given a context. Check if it is valid.
//
//
// Initialize.
//
*pIsValid = FALSE;
ReturnValue = FALSE;
if (i < ATMARP_TABLE_SIZE)
{
pIpEntry = pInterface->pArpTable[i];
while (pIpEntry != NULL_PATMARP_IP_ENTRY)
{
if (pIpEntry == pTargetIpEntry)
{
*pIsValid = TRUE;
ReturnValue = TRUE;
break;
}
else
{
pIpEntry = pIpEntry->pNextEntry;
}
}
}
}
return (ReturnValue);
}
BOOLEAN
AtmArpReadNextTableEntry(
IN PVOID QueryContext,
IN PATMARP_INTERFACE pInterface,
IN PUCHAR pSpace
)
/*++
Routine Description:
Read the next ARP Table Entry for the specified interface. The QueryContext
tells us which entry is to be read.
Arguments:
QueryContext - Indicates the entry to be read.
pInterface - The IF on which the query is being performed
pSpace - where we copy in the desired table entry.
Return Value:
TRUE if the ARP Table has entries beyond the indicated one, FALSE
otherwise.
--*/
{
IPNMEContext *pNMContext;
IPNetToMediaEntry *pIPNMEntry;
PATMARP_IP_ENTRY pIpEntry;
UINT i;
BOOLEAN ReturnValue;
pNMContext = (IPNMEContext *)QueryContext;
pIPNMEntry = (IPNetToMediaEntry *)pSpace;
pIpEntry = (PATMARP_IP_ENTRY)(pNMContext->inc_entry);
AA_STRUCT_ASSERT(pIpEntry, aip);
pIPNMEntry->inme_index = pInterface->IFIndex;
pIPNMEntry->inme_addr = pIpEntry->IPAddress;
if (AA_IS_FLAG_SET(pIpEntry->Flags, AA_IP_ENTRY_STATE_MASK, AA_IP_ENTRY_RESOLVED))
{
AADEBUGP(AAD_EXTRA_LOUD, ("ReadNext: found IP Entry 0x%x, Addr %d.%d.%d.%d\n",
pIpEntry,
((PUCHAR)(&(pIpEntry->IPAddress)))[0],
((PUCHAR)(&(pIpEntry->IPAddress)))[1],
((PUCHAR)(&(pIpEntry->IPAddress)))[2],
((PUCHAR)(&(pIpEntry->IPAddress)))[3]
));
pIPNMEntry->inme_physaddrlen = AA_ATM_PHYSADDR_LEN;
AA_ASSERT(pIpEntry->pAtmEntry != NULL_PATMARP_ATM_ENTRY);
AA_COPY_MEM(pIPNMEntry->inme_physaddr,
&pIpEntry->pAtmEntry->ATMAddress.Address[AA_ATM_ESI_OFFSET],
AA_ATM_PHYSADDR_LEN);
if (pIpEntry->Flags & AA_IP_ENTRY_IS_STATIC)
{
pIPNMEntry->inme_type = INME_TYPE_STATIC;
}
else
{
pIPNMEntry->inme_type = INME_TYPE_DYNAMIC;
}
}
else
{
pIPNMEntry->inme_physaddrlen = 0;
pIPNMEntry->inme_type = INME_TYPE_INVALID;
}
//
// Update the context for the next entry.
//
if (pIpEntry->pNextEntry != NULL_PATMARP_IP_ENTRY)
{
pNMContext->inc_entry = pIpEntry->pNextEntry;
ReturnValue = TRUE;
}
else
{
//
// Initialize.
ReturnValue = FALSE;
i = pNMContext->inc_index + 1;
pNMContext->inc_index = 0;
pNMContext->inc_entry = NULL;
while (i < ATMARP_TABLE_SIZE)
{
if (pInterface->pArpTable[i] != NULL_PATMARP_IP_ENTRY)
{
pNMContext->inc_entry = pInterface->pArpTable[i];
pNMContext->inc_index = i;
ReturnValue = TRUE;
break;
}
else
{
i++;
}
}
}
return (ReturnValue);
}
VOID
AtmArpReStartInterface(
IN PNDIS_WORK_ITEM pWorkItem,
IN PVOID IfContext
)
/*++
Routine Description:
Bring back up the IP interface.
Arguments:
pWorkItem
IfContextw - The IF, which is expected to have reconfig
state RECONFIG_QUEUED.
Return Value:
None
--*/
{
PATMARP_INTERFACE pInterface;
ULONG rc;
BOOLEAN fRestart = FALSE;
#if DBG
AA_IRQL EntryIrq, ExitIrq;
#endif
AA_GET_ENTRY_IRQL(EntryIrq);
#if !BINARY_COMPATIBLE
AA_ASSERT(EntryIrq == PASSIVE_LEVEL);
#endif
pInterface = (PATMARP_INTERFACE)IfContext;
AA_STRUCT_ASSERT(pInterface, aai);
AA_FREE_MEM(pWorkItem);
AA_ACQUIRE_IF_LOCK(pInterface);
if (pInterface->ReconfigState != RECONFIG_RESTART_QUEUED)
{
//
// Shouldn't get here.
//
AA_ASSERT(FALSE);
}
else
{
pInterface->ReconfigState = RECONFIG_RESTART_PENDING;
fRestart = TRUE;
}
rc = AtmArpDereferenceInterface(pInterface); // Reconfig Work item
AADEBUGP(AAD_WARNING, ("RestartIF: IF %x/%x, fRestart %d, rc %d\n",
pInterface, pInterface->Flags, fRestart, rc));
//
// If we're restarting, there should be at least 2 references to the
// pInterface -- 1- the old carryover and 2- the pending completion
// completion of the reconfig event.
//
if (rc < 2 || !fRestart)
{
//
// Must be at least 2 if we're in the middle of a reconfig!
//
AA_ASSERT(!fRestart);
if (rc != 0)
{
AA_RELEASE_IF_LOCK(pInterface);
}
}
else
{
//
// At this point we know that we are doing a restart of the interface.
//
// We will extract the pointer to the adapter, the
// configuration string for the interface, and the pointer to the
// pending netPnpEvent (we'll need these later),
// and then do a FORCED DEALLOCATION of the interface.
// We will then allocate the interface. We go through this
// deallocate-allocate sequence to make sure that the interface
// structure and all it's sub-structures are properly initialized.
//
// We could have tried to re-use the old interface, but if so we
// would have to write code to clean up the old interface. Given
// that we expect restarting of the interface to be an infrequent
// event, it is more important to conserve code size in this case.
//
NDIS_STRING IPConfigString = pInterface->IPConfigString; // struct copy
PATMARP_ADAPTER pAdapter = pInterface->pAdapter;
PNET_PNP_EVENT pReconfigEvent = pInterface->pReconfigEvent;
NDIS_STATUS Status = NDIS_STATUS_SUCCESS;
do
{
NDIS_HANDLE LISConfigHandle;
rc = AtmArpDereferenceInterface(pInterface);
if (rc)
{
rc = AtmArpDereferenceInterface(pInterface);
}
if (rc != 0)
{
AA_RELEASE_IF_LOCK(pInterface);
}
pInterface = NULL;
//
// Open the configuration section for this LIS.
//
LISConfigHandle = AtmArpCfgOpenLISConfigurationByName(
pAdapter,
&IPConfigString
);
if (LISConfigHandle == NULL)
{
//
// This is the normal termination condition, i.e.
// we reached the end of the LIS list for this
// adapter.
//
AADEBUGP(AAD_INFO, ("ReStartInterface: cannot open LIS\n"));
Status = NDIS_STATUS_FAILURE;
break;
}
pInterface = AtmArpAddInterfaceToAdapter (
pAdapter,
LISConfigHandle,
&IPConfigString
);
//
// Close the configuration section for this LIS.
//
AtmArpCfgCloseLISConfiguration(LISConfigHandle);
LISConfigHandle = NULL;
if (pInterface == NULL_PATMARP_INTERFACE)
{
Status = NDIS_STATUS_FAILURE;
break;
}
} while(FALSE);
#ifdef _PNP_POWER_
//
// Complete the pending PnPReconfig event, if any.
//
if (pReconfigEvent)
{
NdisCompletePnPEvent(
Status,
pAdapter->NdisAdapterHandle,
pReconfigEvent
);
AADEBUGP( AAD_INFO,
("ReStartInterface: IF 0x%x, Status 0x%x, Event 0x%x\n",
pInterface, Status, pReconfigEvent));
}
#else
AA_ASSERT(!pReconfigEvent);
#endif
}
}