windows-nt/Source/XPSP1/NT/net/rras/ip/sample/networkmgr.c
2020-09-26 16:20:57 +08:00

831 lines
22 KiB
C

/*++
Copyright (c) 1999, Microsoft Corporation
Module Name:
sample\networkmanager.c
Abstract:
The file contains network configuration related functions,
implementing the network manager.
NOTE: The network manager should never take the configuration entry
lock (g_ce.rwlLock).
. the protocol manager never modifies any g_ce field protected by it
. the network manager never modifies any g_ce field protected by it
. the configuration manager never cleans up any g_ce field as long as
there are active threads
--*/
#include "pchsample.h"
#pragma hdrstop
BOOL
ValidateInterfaceConfig (
IN PIPSAMPLE_IF_CONFIG piic)
/*++
Routine Description
Checks to see if the interface configuration is OK. It is good practice
to do this because a corrupt registry can change configuration causing
all sorts of debugging headaches if it is not found early
Locks
None
Arguments
piic pointer to ip sample interface's configuration
Return Value
TRUE if the configuration is good
FALSE o/w
--*/
{
DWORD dwErr = NO_ERROR;
do // breakout loop
{
if (piic is NULL)
{
dwErr = ERROR_INVALID_PARAMETER;
TRACE0(NETWORK, "Error null interface config");
break;
}
//
// check range of each field
//
// ensure that the metric is within bounds
if (piic->ulMetric > IPSAMPLE_METRIC_INFINITE)
{
dwErr = ERROR_INVALID_PARAMETER;
TRACE0(NETWORK, "Error metric out of range");
break;
}
// ensure that protocol flags are fine, for now they'll always be
// add more here...
} while (FALSE);
if (!(dwErr is NO_ERROR))
{
TRACE0(NETWORK, "Error corrupt interface config");
LOGERR0(CORRUPT_INTERFACE_CONFIG, dwErr);
return FALSE;
}
return TRUE;
}
////////////////////////////////////////
// CALLBACKFUNCTIONS
////////////////////////////////////////
VOID
WINAPI
NM_CallbackNetworkEvent (
IN PVOID pvContext,
IN BOOLEAN bTimerOrWaitFired)
/*++
Routine Description
Processes a network event on the specified interface.
NOTE: The interface might have been deleted.
Locks
Acquires shared (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
pvContext dwIfIndex
Return Value
None
--*/
{
DWORD dwErr = NO_ERROR;
DWORD dwIfIndex = 0;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
PACKET Packet;
dwIfIndex = (DWORD) pvContext;
TRACE1(ENTER, "Entering NM_CallbackNetworkEvent: %u", dwIfIndex);
if (!ENTER_SAMPLE_API()) { return; } // cannot return anything
ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwIfIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// fail if interface is inactive
if (!INTERFACE_IS_ACTIVE(pieInterfaceEntry))
{
TRACE1(NETWORK, "Error interface %u is inactive", dwIfIndex);
break;
}
RTASSERT(pieInterfaceEntry->sRawSocket != INVALID_SOCKET);
if (SocketReceiveEvent(pieInterfaceEntry->sRawSocket))
{
if (SocketReceive(pieInterfaceEntry->sRawSocket,
&Packet) is NO_ERROR)
PacketDisplay(&Packet);
}
else
{
TRACE1(NETWORK, "Error interface %u false alarm", dwIfIndex);
break;
}
} while (FALSE);
// reregister ReceiveWait if the interface exists
if (pieInterfaceEntry)
{
if (!RegisterWaitForSingleObject(&pieInterfaceEntry->hReceiveWait,
pieInterfaceEntry->hReceiveEvent,
NM_CallbackNetworkEvent,
(PVOID) pieInterfaceEntry->dwIfIndex,
INFINITE,
WT_EXECUTEONLYONCE))
{
dwErr = GetLastError();
TRACE2(NETWORK, "Error %u registering wait for %u, continuing",
dwErr, pieInterfaceEntry->dwIfIndex);
LOGERR0(REGISTER_WAIT_FAILED, dwErr);
}
}
RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
TRACE0(LEAVE, "Leaving NM_CallbackNetworkEvent");
}
VOID
WINAPI
NM_CallbackPeriodicTimer (
IN PVOID pvContext,
IN BOOLEAN bTimerOrWaitFired)
/*++
Routine Description
Processes a periodic timeout event on the specified interface.
NOTE: The interface might have been deleted.
Locks
Acquires shared (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
pvContext dwIfIndex
Return Value
None
--*/
{
DWORD dwErr = NO_ERROR;
DWORD dwIfIndex = 0;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
PPACKET pPacket;
dwIfIndex = (DWORD) pvContext;
TRACE1(ENTER, "Entering NM_CallbackPeriodicTimer: %u", dwIfIndex);
if (!ENTER_SAMPLE_API()) { return; } // cannot return anything
ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwIfIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// fail if interface is inactive
if (!INTERFACE_IS_ACTIVE(pieInterfaceEntry))
break;
RTASSERT(pieInterfaceEntry->sRawSocket != INVALID_SOCKET);
// fail if packet cannot be created
if (PacketCreate(&pPacket) != NO_ERROR)
break;
PacketDisplay(pPacket);
dwErr = SocketSend(pieInterfaceEntry->sRawSocket,
SAMPLE_PROTOCOL_MULTICAST_GROUP,
pPacket);
if (dwErr != NO_ERROR)
{
PacketDestroy(pPacket);
break;
}
// update interface statistics
InterlockedIncrement(&(pieInterfaceEntry->iisStats.ulNumPackets));
} while (FALSE);
// restart timer if the interface exists and is active
if ((pieInterfaceEntry) and INTERFACE_IS_ACTIVE(pieInterfaceEntry))
{
RESTART_TIMER(pieInterfaceEntry->hPeriodicTimer,
PERIODIC_INTERVAL,
&dwErr);
}
RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
TRACE0(LEAVE, "Leaving NM_CallbackPeriodicTimer");
}
////////////////////////////////////////
// APIFUNCTIONS
////////////////////////////////////////
DWORD
NM_AddInterface (
IN LPWSTR pwszInterfaceName,
IN DWORD dwInterfaceIndex,
IN WORD wAccessType,
IN PVOID pvInterfaceInfo)
/*++
Routine Description
Add an interface with the given configuration to IPSAMPLE. Interface
is created UNBOUND and DISABLED.
Locks
Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
pwszInterfaceName the name of the interface, used for logging.
dwInterfaceIndex the positive integer used to refer to this interface.
wAccessType access type... MULTIACCESS or POINTTOPOINT
pvInterfaceInfo our config for this interface
Return Value
NO_ERROR if successfully initiailzed
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PIPSAMPLE_IF_CONFIG piic = NULL;
PINTERFACE_ENTRY pieEntry = NULL;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
// validate the configuration parameters
if (!ValidateInterfaceConfig(piic))
{
dwErr = ERROR_INVALID_PARAMETER;
break;
}
// fail if the interface exists
if (IE_IsPresent(dwInterfaceIndex))
{
dwErr = ERROR_INVALID_PARAMETER;
TRACE2(NETWORK, "Error interface %S (%u) already exists",
pwszInterfaceName, dwInterfaceIndex);
LOGERR0(INTERFACE_PRESENT, dwErr);
break;
}
// create an interface entry
dwErr = IE_Create(pwszInterfaceName,
dwInterfaceIndex,
wAccessType,
&pieEntry);
if (dwErr != NO_ERROR)
break;
// initialize interface configuration fields
pieEntry->ulMetric = piic->ulMetric;
// insert the interface in all access structures
dwErr = IE_Insert(pieEntry);
RTASSERT(dwErr is NO_ERROR); // no reason to fail!
// update global statistics
InterlockedIncrement(&(g_ce.igsStats.ulNumInterfaces));
} while (FALSE);
RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_DeleteInterface (
IN DWORD dwInterfaceIndex)
/*++
Routine Description
Remove an interface with the given index, deactivating it if required.
Locks
Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
dwInterfaceIndex the positive integer used to identify the interface.
Return Value
NO_ERROR if successfully initiailzed
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
do // breakout loop
{
// remove from all tables, lists...
ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
dwErr = IE_Delete(dwInterfaceIndex, &pieInterfaceEntry);
RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
// fail if the interface does not exist
if (dwErr != NO_ERROR)
{
TRACE1(NETWORK, "Error interface %u does not exist",
dwInterfaceIndex);
break;
}
// destroy the interface entry, deregisters ReceiveWait
// hence best not to hold any locks to prevent deadlocks.
IE_Destroy(pieInterfaceEntry);
// update global statistics
InterlockedDecrement(&(g_ce.igsStats.ulNumInterfaces));
} while (FALSE);
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_InterfaceStatus (
IN DWORD dwInterfaceIndex,
IN BOOL bInterfaceActive,
IN DWORD dwStatusType,
IN PVOID pvStatusInfo)
/*++
Routine Description
Locks
Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
dwInterfaceIndex The index of the interface in question
bInterfaceActive Whether the interface can send and receive data
dwStatusType RIS_INTERFACE_[ADDRESS_CHANGED|ENABLED|DISABLED]
pvStatusInfo Pointer to IP_ADAPTER_BINDING_INFO containing info
about the addresses on the interface
Return Value
NO_ERROR if successfully initiailzed
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
PIP_ADAPTER_BINDING_INFO pBinding = NULL;
BOOL bBindingChanged = FALSE;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// the only status we care about is a change in interface binding
if (dwStatusType is RIS_INTERFACE_ADDRESS_CHANGE)
{
// destroy existing binding
if (INTERFACE_IS_BOUND(pieInterfaceEntry))
{
bBindingChanged = TRUE;
dwErr = IE_UnBindInterface(pieInterfaceEntry);
RTASSERT(dwErr is NO_ERROR);
}
// create new binding
pBinding = (PIP_ADAPTER_BINDING_INFO) pvStatusInfo;
if(pBinding->AddressCount)
{
bBindingChanged = TRUE;
dwErr = IE_BindInterface(pieInterfaceEntry, pBinding);
if (dwErr != NO_ERROR)
break;
}
}
// interface needs to be deactivated even when the binding changes!
// this restriction is due to the fact that the socket is bound to
// the interface address and not the interface index...
if (INTERFACE_IS_ACTIVE(pieInterfaceEntry) and
(bBindingChanged or !bInterfaceActive))
{
dwErr = IE_DeactivateInterface(pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
}
// activate interface only when a binding exists!
// i.e. we do not support unnumbered interfaces for now...
if (INTERFACE_IS_INACTIVE(pieInterfaceEntry) and
INTERFACE_IS_BOUND(pieInterfaceEntry) and
bInterfaceActive)
{
dwErr = IE_ActivateInterface(pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
}
} while (FALSE);
RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_GetInterfaceInfo (
IN DWORD dwInterfaceIndex,
IN PVOID pvInterfaceInfo,
IN OUT PULONG pulBufferSize,
OUT PULONG pulStructureVersion,
OUT PULONG pulStructureSize,
OUT PULONG pulStructureCount)
/*++
Routine Description
See if there's space enough to return ip sample interface config. If
yes, we return it, otherwise return the size needed.
Locks
Acquires shared (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
dwInterfaceIndex the interface whose configuration is needed
pvInterfaceInfo pointer to allocated buffer to store our config
pulBufferSize IN size of buffer received
OUT size of our interface config
Return Value
NO_ERROR if success
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PIPSAMPLE_IF_CONFIG piic;
ULONG ulSize = sizeof(IPSAMPLE_IF_CONFIG);
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// fail if the size was too small or there was no storage
if((*pulBufferSize < ulSize) or (pvInterfaceInfo is NULL))
{
TRACE1(NETWORK, "NM_GetInterfaceInfo: *ulBufferSize %u",
*pulBufferSize);
*pulBufferSize = ulSize;
dwErr = ERROR_INSUFFICIENT_BUFFER;
break;
}
// set the OUT parameters
*pulBufferSize = ulSize;
if (pulStructureVersion) *pulStructureVersion = 1;
if (pulStructureSize) *pulStructureSize = ulSize;
if (pulStructureCount) *pulStructureCount = 1;
// copy out the interface configuration
piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
piic->ulMetric = pieInterfaceEntry->ulMetric;
} while (FALSE);
RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_SetInterfaceInfo (
IN DWORD dwInterfaceIndex,
IN PVOID pvInterfaceInfo)
/*++
Routine Description
Set ip sample interface's configuration.
Locks
Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
dwInterfaceIndex the interface whose configuration is to be set
pvInterfaceInfo buffer with new interface config
Return Value
NO_ERROR if success
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PIPSAMPLE_IF_CONFIG piic;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
ACQUIRE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// fail if the configuration is invalid
piic = (PIPSAMPLE_IF_CONFIG) pvInterfaceInfo;
if(!ValidateInterfaceConfig(piic))
{
dwErr = ERROR_INVALID_PARAMETER;
break;
}
// update our configuration
pieInterfaceEntry->ulMetric = piic->ulMetric;
// might need additional processing depending on the state change
// caused by the updated interface configuration and the protocol
// behavior. for instance, sockets may need to be created/shutdown
} while (FALSE);
RELEASE_WRITE_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_DoUpdateRoutes (
IN DWORD dwInterfaceIndex
)
/*++
Routine Description
Updates routes over a demand dial interface.
Locks
Acquires exclusively (g_ce.pneNetworkEntry)->rwlLock
Releases (g_ce.pneNetworkEntry)->rwlLock
Arguments
dwInterfaceIndex the relevant interface index
Return Value
NO_ERROR if success
Failure code o/w
--*/
{
DWORD dwErr = NO_ERROR;
PINTERFACE_ENTRY pieInterfaceEntry = NULL;
MESSAGE mMessage;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
ACQUIRE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
do // breakout loop
{
// fail if the interface does not exist
dwErr = IE_Get(dwInterfaceIndex, &pieInterfaceEntry);
if (dwErr != NO_ERROR)
break;
// ensure interface is active
if (INTERFACE_IS_INACTIVE(pieInterfaceEntry))
{
dwErr = ERROR_CAN_NOT_COMPLETE;
TRACE1(NETWORK, "Error, interface %u inactive", dwInterfaceIndex);
break;
}
// here we do protocol specific processing,
// for sample nothing :)
} while (FALSE);
RELEASE_READ_LOCK(&((g_ce.pneNetworkEntry)->rwlLock));
mMessage.UpdateCompleteMessage.InterfaceIndex = dwInterfaceIndex;
mMessage.UpdateCompleteMessage.UpdateType = RF_DEMAND_UPDATE_ROUTES;
mMessage.UpdateCompleteMessage.UpdateStatus = dwErr;
if (EnqueueEvent(UPDATE_COMPLETE, mMessage) is NO_ERROR)
SetEvent(g_ce.hMgrNotificationEvent);
LEAVE_SAMPLE_API();
return dwErr;
}
DWORD
NM_ProcessRouteChange (
VOID)
/*++
Routine Description
Handle messages from RTM about route changes.
Locks
None
Arguments
None
Return Value
NO_ERROR success
Error Code o/w
--*/
{
DWORD dwErr = NO_ERROR;
RTM_DEST_INFO rdiDestination; // 1 view registered for change
BOOL bDone = FALSE;
UINT uiNumDests;
if (!ENTER_SAMPLE_API()) { return ERROR_CAN_NOT_COMPLETE; }
// loop dequeueing messages until RTM says there are no more left
while (!bDone)
{
// retrieve route changes
uiNumDests = 1;
dwErr = RTM_GetChangedDests(
g_ce.hRtmHandle, // my RTMv2 handle
g_ce.hRtmNotificationHandle, // my notification handle
&uiNumDests, // IN # dest info's required
// OUT # dest info's supplied
&rdiDestination); // OUT buffer for dest info's
switch (dwErr)
{
case ERROR_NO_MORE_ITEMS:
bDone = TRUE;
dwErr = NO_ERROR;
if (uiNumDests < 1)
break;
// else continue below to process the last destination
case NO_ERROR:
RTASSERT(uiNumDests is 1);
RTM_DisplayDestInfo(&rdiDestination);
// release the destination info
if (RTM_ReleaseChangedDests(
g_ce.hRtmHandle, // my RTMv2 handle
g_ce.hRtmNotificationHandle,// my notif handle
uiNumDests, // 1
&rdiDestination // released dest info
) != NO_ERROR)
TRACE0(NETWORK, "Error releasing changed dests");
break;
default:
bDone = TRUE;
TRACE1(NETWORK, "Error %u RtmGetChangedDests", dwErr);
break;
}
} // while
LEAVE_SAMPLE_API();
return dwErr;
}