mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1791 lines
44 KiB
1791 lines
44 KiB
/*******************************************************************/
|
|
/* Copyright(c) 1993 Microsoft Corporation */
|
|
/*******************************************************************/
|
|
|
|
//***
|
|
//
|
|
// Filename: ipxcp.c
|
|
//
|
|
// Description: implements the IPX network layer configuration
|
|
//
|
|
//
|
|
// Author: Stefan Solomon (stefans) November 24, 1993.
|
|
//
|
|
// Revision History:
|
|
//
|
|
//***
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
// keep track is we already have an active dialout port as a client
|
|
DWORD WorkstationDialoutActive = 0;
|
|
|
|
// Keep track of the number of clients currently connected
|
|
DWORD dwClientCount = 0;
|
|
|
|
// Used to assign remote wan workstations node numbers
|
|
extern DWORD LastNodeAssigned;
|
|
extern BOOL bAssignSpecificNode;
|
|
|
|
VOID (*PPPCompletionRoutine)(HCONN hPortOrBundle,
|
|
DWORD Protocol,
|
|
PPP_CONFIG * pSendConfig,
|
|
DWORD dwError);
|
|
|
|
HANDLE PPPThreadHandle = INVALID_HANDLE_VALUE;
|
|
|
|
// Handle to queue that holds configuration changes that need to
|
|
// be made when the client count goes to zero next.
|
|
HANDLE hConfigQueue = NULL;
|
|
|
|
// Function obtained from the router manager to update global
|
|
// config
|
|
extern DWORD (WINAPI *RmUpdateIpxcpConfig)(PIPXCP_ROUTER_CONFIG_PARAMS pParams);
|
|
|
|
HANDLE g_hRouterLog = NULL;
|
|
|
|
DWORD
|
|
WanNetReconfigure();
|
|
|
|
DWORD
|
|
IpxCpBegin(OUT VOID **ppWorkBuf,
|
|
IN VOID *pInfo);
|
|
|
|
DWORD
|
|
IpxCpEnd(IN VOID *pWorkBuffer);
|
|
|
|
DWORD
|
|
IpxCpReset(IN VOID *pWorkBuffer);
|
|
|
|
DWORD
|
|
IpxCpThisLayerUp(IN VOID *pWorkBuffer);
|
|
|
|
DWORD
|
|
IpxCpThisLayerDown(IN VOID *pWorkBuffer);
|
|
|
|
DWORD
|
|
IpxCpMakeConfigRequest(IN VOID *pWorkBuffer,
|
|
OUT PPP_CONFIG *pRequestBufffer,
|
|
IN DWORD cbRequestBuffer);
|
|
|
|
DWORD
|
|
IpxCpMakeConfigResult(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer,
|
|
OUT PPP_CONFIG *pResultBuffer,
|
|
IN DWORD cbResultBuffer,
|
|
IN BOOL fRejectNaks);
|
|
|
|
DWORD
|
|
IpxCpConfigNakReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer);
|
|
|
|
DWORD
|
|
IpxCpConfigAckReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer);
|
|
|
|
DWORD
|
|
IpxCpConfigRejReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer);
|
|
|
|
DWORD
|
|
IpxCpGetNegotiatedInfo(IN VOID *pWorkBuffer,
|
|
OUT VOID * pIpxCpResult );
|
|
|
|
DWORD
|
|
IpxCpProjectionNotification(IN VOID *pWorkBuf,
|
|
IN VOID *pProjectionResult);
|
|
|
|
#define ERROR_INVALID_OPTION 1
|
|
#define ERROR_INVALID_OPTLEN 2
|
|
|
|
DWORD
|
|
ValidOption(UCHAR option,
|
|
UCHAR optlen);
|
|
|
|
BOOL
|
|
DesiredOption(UCHAR option, USHORT *indexp);
|
|
|
|
USHORT
|
|
DesiredConfigReqLength();
|
|
|
|
DWORD
|
|
IpxCpUpdateGlobalConfig( VOID );
|
|
|
|
DWORD
|
|
IpxcpUpdateQueuedGlobalConfig();
|
|
|
|
// Update Flags
|
|
#define FLAG_UPDATE_WANNET 0x1
|
|
#define FLAG_UPDATE_ROUTER 0x2
|
|
|
|
typedef BOOL (*OPTION_HANDLER)(PUCHAR optptr,
|
|
PIPXCP_CONTEXT contextp,
|
|
PUCHAR resptr,
|
|
OPT_ACTION Action);
|
|
|
|
|
|
static OPTION_HANDLER OptionHandler[] =
|
|
{
|
|
NULL,
|
|
NetworkNumberHandler,
|
|
NodeNumberHandler,
|
|
CompressionProtocolHandler,
|
|
RoutingProtocolHandler,
|
|
NULL, // RouterName - not a DESIRED parammeter
|
|
ConfigurationCompleteHandler
|
|
};
|
|
|
|
UCHAR nullnet[] = { 0x00, 0x00, 0x00, 0x00 };
|
|
UCHAR nullnode[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
|
|
|
USHORT MaxDesiredParameters = MAX_DESIRED_PARAMETERS;
|
|
|
|
CRITICAL_SECTION DbaseCritSec;
|
|
|
|
//*** Declarations and defs for the options to be negotiated with this
|
|
// version of IPXCP
|
|
|
|
UCHAR DesiredParameter[MAX_DESIRED_PARAMETERS] = {
|
|
|
|
IPX_NETWORK_NUMBER,
|
|
IPX_NODE_NUMBER,
|
|
IPX_COMPRESSION_PROTOCOL
|
|
};
|
|
|
|
USHORT DesiredParameterLength[MAX_DESIRED_PARAMETERS] = {
|
|
|
|
6, // IPX_NETWORK_NUMBER,
|
|
8, // IPX_NODE_NUMBER,
|
|
4 // IPX_COMPRESSION_PROTOCOL
|
|
};
|
|
|
|
DWORD
|
|
IpxCpInit(BOOL fInitialize)
|
|
{
|
|
static DWORD dwRefCount = 0;
|
|
|
|
if (fInitialize)
|
|
{
|
|
if (0 == dwRefCount)
|
|
{
|
|
//
|
|
// Read the registry parameters and set IpxCp configuration
|
|
//
|
|
|
|
InitializeCriticalSection(&DbaseCritSec);
|
|
|
|
g_hRouterLog = RouterLogRegisterW(L"IPXCP");
|
|
|
|
StartTracing();
|
|
|
|
GetIpxCpParameters(&GlobalConfig);
|
|
|
|
SS_DBGINITIALIZE;
|
|
|
|
InitializeRouterManagerIf();
|
|
|
|
InitializeNodeHT();
|
|
|
|
InitializeConnHT();
|
|
|
|
LoadIpxWan();
|
|
|
|
CQCreate (&hConfigQueue);
|
|
}
|
|
|
|
dwRefCount++;
|
|
}
|
|
else
|
|
{
|
|
dwRefCount--;
|
|
|
|
if (0 == dwRefCount)
|
|
{
|
|
//
|
|
// Release the global list of routes
|
|
//
|
|
|
|
CQCleanup (hConfigQueue);
|
|
UnloadIpxWan ();
|
|
StopTracing();
|
|
g_hRouterLog = NULL;
|
|
|
|
DeleteCriticalSection(&DbaseCritSec);
|
|
}
|
|
}
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
DWORD
|
|
IpxCpGetInfo(
|
|
IN DWORD dwProtocolId,
|
|
OUT PPPCP_INFO *pCpInfo)
|
|
{
|
|
if (dwProtocolId != PPP_IPXCP_PROTOCOL)
|
|
return(ERROR_INVALID_PARAMETER);
|
|
|
|
ZeroMemory(pCpInfo, sizeof(PPPCP_INFO));
|
|
|
|
pCpInfo->Protocol = PPP_IPXCP_PROTOCOL;
|
|
lstrcpy(pCpInfo->SzProtocolName, "IPXCP");
|
|
pCpInfo->Recognize = CODE_REJ + 1;
|
|
pCpInfo->RasCpInit = IpxCpInit;
|
|
pCpInfo->RasCpBegin = IpxCpBegin;
|
|
pCpInfo->RasCpEnd = IpxCpEnd;
|
|
pCpInfo->RasCpReset = IpxCpReset;
|
|
pCpInfo->RasCpThisLayerUp = IpxCpThisLayerUp;
|
|
pCpInfo->RasCpThisLayerDown = IpxCpThisLayerDown;
|
|
pCpInfo->RasCpMakeConfigRequest = IpxCpMakeConfigRequest;
|
|
pCpInfo->RasCpMakeConfigResult = IpxCpMakeConfigResult;
|
|
pCpInfo->RasCpConfigAckReceived = IpxCpConfigAckReceived;
|
|
pCpInfo->RasCpConfigNakReceived = IpxCpConfigNakReceived;
|
|
pCpInfo->RasCpConfigRejReceived = IpxCpConfigRejReceived;
|
|
pCpInfo->RasCpGetNegotiatedInfo = IpxCpGetNegotiatedInfo;
|
|
pCpInfo->RasCpProjectionNotification = IpxCpProjectionNotification;
|
|
pCpInfo->RasCpChangeNotification = IpxCpUpdateGlobalConfig;
|
|
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
//***
|
|
//
|
|
// Function: IpxCpBegin
|
|
//
|
|
// Descr: Called when a line is connected.
|
|
//
|
|
//***
|
|
|
|
DWORD
|
|
IpxCpBegin(OUT VOID **ppWorkBuf,
|
|
IN VOID *pInfo)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
PPPPCP_INIT initp;
|
|
DWORD err;
|
|
DWORD tickcount;
|
|
int i;
|
|
ULONG InterfaceType;
|
|
ULONG ConnectionId;
|
|
|
|
initp = (PPPPCP_INIT)pInfo;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpBegin: Entered for if # %d\n", initp->hInterface);
|
|
|
|
// Get the completion routine and the thread handle
|
|
if(PPPThreadHandle == INVALID_HANDLE_VALUE) {
|
|
|
|
// not initialized
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(),
|
|
GetCurrentThread(),
|
|
GetCurrentProcess(),
|
|
&PPPThreadHandle,
|
|
0,
|
|
FALSE,
|
|
DUPLICATE_SAME_ACCESS )) {
|
|
|
|
return GetLastError();
|
|
}
|
|
|
|
PPPCompletionRoutine = initp->CompletionRoutine;
|
|
}
|
|
|
|
//
|
|
// Get the Connection Id (Bundle id)
|
|
//
|
|
|
|
ConnectionId = HandleToUlong(initp->hConnection);
|
|
|
|
//
|
|
// Determine the connection type
|
|
//
|
|
|
|
if((InterfaceType = GetInterfaceType(initp)) == IF_TYPE_OTHER) {
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
if((InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) ||
|
|
(InterfaceType == IF_TYPE_STANDALONE_WORKSTATION_DIALOUT)) {
|
|
|
|
// If we are configured to allow only one dialout net and if we are
|
|
// already dialed out once, we disable further dialouts.
|
|
if(GlobalConfig.SingleClientDialout && IsWorkstationDialoutActive()) {
|
|
|
|
return ERROR_IPXCP_DIALOUT_ALREADY_ACTIVE;
|
|
}
|
|
}
|
|
|
|
if(initp->fServer &&
|
|
(!IsRouterStarted())) {
|
|
|
|
// we cannot accept dialin on machines without the router started
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
// allocate a context structure to be used as work buffer for this connection
|
|
if((contextp = (PIPXCP_CONTEXT)GlobalAlloc(GPTR, sizeof(IPXCP_CONTEXT))) == NULL) {
|
|
|
|
*ppWorkBuf = NULL;
|
|
return (ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
*ppWorkBuf = (VOID *)contextp;
|
|
|
|
// allocate a route for this connection to the IPX stack
|
|
if(err = RmAllocateRoute(HandleToUlong(initp->hPort))) {
|
|
|
|
// cannot allocate route
|
|
*ppWorkBuf = NULL;
|
|
GlobalFree(contextp);
|
|
return err;
|
|
}
|
|
|
|
//
|
|
// Set up common context part
|
|
//
|
|
|
|
// hInterface is always an index
|
|
|
|
contextp->Config.InterfaceIndex = HandleToUlong(initp->hInterface);
|
|
|
|
if(InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) {
|
|
|
|
if(AddLocalWkstaDialoutInterface(&contextp->Config.InterfaceIndex) != NO_ERROR) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpBegin: AddLocalWkstaDialoutInterface failed !\n");
|
|
RmDeallocateRoute(HandleToUlong(initp->hConnection));
|
|
GlobalFree(contextp);
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
}
|
|
|
|
contextp->hPort = HandleToUlong(initp->hPort);
|
|
contextp->hConnection = initp->hConnection;
|
|
|
|
contextp->InterfaceType = InterfaceType;
|
|
|
|
contextp->RouteState = ROUTE_ALLOCATED;
|
|
contextp->IpxwanState = IPXWAN_NOT_STARTED;
|
|
contextp->ErrorLogged = FALSE;
|
|
contextp->NetNumberNakSentCount = 0;
|
|
contextp->NetNumberNakReceivedCount = 0;
|
|
|
|
contextp->CompressionProtocol = TELEBIT_COMPRESSED_IPX;
|
|
contextp->SetReceiveCompressionProtocol = FALSE; // no compression initially
|
|
contextp->SetSendCompressionProtocol = FALSE;
|
|
|
|
// mark all our desired parameters as negotiable
|
|
for(i=0; i<MAX_DESIRED_PARAMETERS; i++) {
|
|
|
|
contextp->DesiredParameterNegotiable[i] = TRUE;
|
|
}
|
|
|
|
if(!GlobalConfig.EnableCompressionProtocol) {
|
|
|
|
contextp->DesiredParameterNegotiable[IPX_COMPRESSION_PROTOCOL_INDEX] = FALSE;
|
|
}
|
|
|
|
contextp->NodeHtLinkage.Flink = NULL;
|
|
contextp->NodeHtLinkage.Blink = NULL;
|
|
|
|
contextp->Config.ConnectionId = ConnectionId;
|
|
|
|
contextp->AllocatedNetworkIndex = INVALID_NETWORK_INDEX;
|
|
|
|
// check if this is an IPXWAN connection
|
|
contextp->Config.IpxwanConfigRequired = 0;
|
|
|
|
if((InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) ||
|
|
(InterfaceType == IF_TYPE_STANDALONE_WORKSTATION_DIALOUT)) {
|
|
|
|
if(GlobalConfig.EnableIpxwanForWorkstationDialout) {
|
|
|
|
contextp->Config.IpxwanConfigRequired = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(GetIpxwanInterfaceConfig(contextp->Config.InterfaceIndex,
|
|
&contextp->Config.IpxwanConfigRequired) != NO_ERROR) {
|
|
|
|
RmDeallocateRoute(HandleToUlong(initp->hConnection));
|
|
GlobalFree(contextp);
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
}
|
|
|
|
if(contextp->Config.IpxwanConfigRequired &&
|
|
!IpxWanDllHandle) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpBegin: IPXWAN Config Required but IPXWAN.DLL not loaded");
|
|
|
|
RmDeallocateRoute(HandleToUlong(initp->hConnection));
|
|
GlobalFree(contextp);
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
contextp->IpxConnectionHandle = 0xFFFFFFFF;
|
|
|
|
//
|
|
// Set up the remaining context according to Dialin/Dialout role
|
|
//
|
|
if(initp->fServer) {
|
|
|
|
//*** DIALIN ***
|
|
|
|
if(!contextp->Config.IpxwanConfigRequired) {
|
|
|
|
// allocate/generate the connection's WAN net number according to the router configuration
|
|
if(GetWanNetNumber(contextp->Config.Network,
|
|
&contextp->AllocatedNetworkIndex,
|
|
contextp->InterfaceType) != NO_ERROR) {
|
|
|
|
if(contextp->InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) {
|
|
|
|
DeleteLocalWkstaDialoutInterface(contextp->Config.InterfaceIndex);
|
|
}
|
|
|
|
RmDeallocateRoute(HandleToUlong(initp->hConnection));
|
|
GlobalFree(contextp);
|
|
|
|
return ERROR_CAN_NOT_COMPLETE;
|
|
}
|
|
|
|
// set up the local server node value
|
|
contextp->Config.LocalNode[5] = 1;
|
|
|
|
// set up the remote client node value
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
// if we have been given a specific node to handout
|
|
// to clients, assign it here
|
|
if (bAssignSpecificNode) {
|
|
memcpy (contextp->Config.RemoteNode, GlobalConfig.puSpecificNode, 6);
|
|
}
|
|
|
|
// Otherwise, assign a random node number
|
|
else {
|
|
LastNodeAssigned++;
|
|
|
|
PUTULONG2LONG(&contextp->Config.RemoteNode[2], LastNodeAssigned);
|
|
contextp->Config.RemoteNode[0] = 0x02;
|
|
contextp->Config.RemoteNode[1] = 0xEE;
|
|
}
|
|
|
|
// if global wan net -> insert this context buffer in the node hash table.
|
|
if((contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) &&
|
|
GlobalConfig.RParams.EnableGlobalWanNet)
|
|
{
|
|
// Try until we get a unique node number
|
|
while(!NodeIsUnique(contextp->Config.RemoteNode)) {
|
|
LastNodeAssigned++;
|
|
PUTULONG2LONG(&contextp->Config.RemoteNode[2], LastNodeAssigned);
|
|
}
|
|
AddToNodeHT(contextp);
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
}
|
|
else
|
|
{
|
|
// we'll have IPXWAN config on this line
|
|
// set up the remote client node value if the remote is a wksta
|
|
|
|
if(contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) {
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
LastNodeAssigned++;
|
|
|
|
PUTULONG2LONG(&contextp->Config.RemoteNode[2], LastNodeAssigned);
|
|
contextp->Config.RemoteNode[0] = 0x02;
|
|
contextp->Config.RemoteNode[1] = 0xEE;
|
|
|
|
if(GlobalConfig.RParams.EnableGlobalWanNet) {
|
|
|
|
// Try until we get a unique node number
|
|
while(!NodeIsUnique(contextp->Config.RemoteNode)) {
|
|
|
|
LastNodeAssigned++;
|
|
PUTULONG2LONG(&contextp->Config.RemoteNode[2], LastNodeAssigned);
|
|
}
|
|
|
|
AddToNodeHT(contextp);
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
}
|
|
}
|
|
|
|
contextp->Config.ConnectionClient = 0;
|
|
}
|
|
else
|
|
{
|
|
//*** DIALOUT ***
|
|
|
|
if(!contextp->Config.IpxwanConfigRequired) {
|
|
|
|
// set up the context for the client
|
|
// no network allocated
|
|
contextp->AllocatedNetworkIndex = INVALID_NETWORK_INDEX;
|
|
|
|
// default network is null for all cases except cisco router client
|
|
memcpy(contextp->Config.Network, nullnet, 4);
|
|
|
|
contextp->Config.RemoteNode[5] = 1; // server node value
|
|
|
|
// set up the value to be requested as the client node
|
|
tickcount = GetTickCount();
|
|
|
|
PUTULONG2LONG(&contextp->Config.LocalNode[2], tickcount);
|
|
contextp->Config.LocalNode[0] = 0x02;
|
|
contextp->Config.LocalNode[1] = 0xEE;
|
|
}
|
|
|
|
contextp->Config.ConnectionClient = 1;
|
|
|
|
if((contextp->InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) ||
|
|
(contextp->InterfaceType == IF_TYPE_STANDALONE_WORKSTATION_DIALOUT)) {
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
WorkstationDialoutActive++;
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
}
|
|
|
|
// disable the browser on ipx and netbios
|
|
DisableRestoreBrowserOverIpx(contextp, TRUE);
|
|
DisableRestoreBrowserOverNetbiosIpx(contextp, TRUE);
|
|
}
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
AddToConnHT(contextp);
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
//***
|
|
//
|
|
// Function: IpxCpEnd
|
|
//
|
|
// Descr: Called when the line gets disconnected
|
|
//
|
|
//***
|
|
|
|
DWORD
|
|
IpxCpEnd(IN VOID *pWorkBuffer)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
DWORD err;
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpEnd: Entered for if # %d\n", contextp->Config.InterfaceIndex);
|
|
|
|
if(!contextp->Config.ConnectionClient) {
|
|
|
|
//*** DIALIN Clean-Up ***
|
|
|
|
if(!contextp->Config.IpxwanConfigRequired) {
|
|
|
|
// if wan net allocated, release the wan net
|
|
if(contextp->AllocatedNetworkIndex != INVALID_NETWORK_INDEX) {
|
|
|
|
ReleaseWanNetNumber(contextp->AllocatedNetworkIndex);
|
|
}
|
|
}
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
if((contextp->InterfaceType == IF_TYPE_WAN_WORKSTATION) &&
|
|
GlobalConfig.RParams.EnableGlobalWanNet) {
|
|
|
|
RemoveFromNodeHT(contextp);
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
}
|
|
else
|
|
{
|
|
//*** DIALOUT Clean-Up ***
|
|
|
|
if((contextp->InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) ||
|
|
(contextp->InterfaceType == IF_TYPE_STANDALONE_WORKSTATION_DIALOUT)) {
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
WorkstationDialoutActive--;
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
}
|
|
|
|
// restore the browser on ipx and netbios
|
|
DisableRestoreBrowserOverIpx(contextp, FALSE);
|
|
DisableRestoreBrowserOverNetbiosIpx(contextp, FALSE);
|
|
}
|
|
|
|
// we count on the route being de-allocated when the line gets disconnected
|
|
err = RmDeallocateRoute(HandleToUlong(contextp->hConnection));
|
|
|
|
if(contextp->InterfaceType == IF_TYPE_ROUTER_WORKSTATION_DIALOUT) {
|
|
|
|
DeleteLocalWkstaDialoutInterface(contextp->Config.InterfaceIndex);
|
|
}
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
RemoveFromConnHT(contextp);
|
|
}
|
|
|
|
// free the work buffer
|
|
if(GlobalFree(contextp)) {
|
|
|
|
SS_ASSERT(FALSE);
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
DWORD
|
|
IpxCpReset(IN VOID *pWorkBuffer)
|
|
{
|
|
return(NO_ERROR);
|
|
}
|
|
|
|
DWORD
|
|
IpxCpProjectionNotification(IN VOID *pWorkBuffer,
|
|
IN VOID *pProjectionResult)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
//***
|
|
//
|
|
// Function: IpxThisLayerUp
|
|
//
|
|
// Descr: Called when the IPXCP negotiation has been SUCCESSFULY
|
|
// completed
|
|
//
|
|
//***
|
|
|
|
|
|
DWORD
|
|
IpxCpThisLayerUp(IN VOID *pWorkBuffer)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
DWORD err;
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
dwClientCount++;
|
|
TraceIpx(PPPIF_TRACE, "IpxCpThisLayerUp: Entered for if # %d (%d total)\n",
|
|
contextp->Config.InterfaceIndex, dwClientCount);
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
//
|
|
//*** Configuration done with IPXWAN ***
|
|
//
|
|
|
|
ACQUIRE_DATABASE_LOCK;
|
|
|
|
switch(contextp->IpxwanState) {
|
|
|
|
case IPXWAN_NOT_STARTED:
|
|
TraceIpx(
|
|
PPPIF_TRACE,
|
|
"IpxCpThisLayerUp: Do LINEUP on if #%d. IPXWAN completes config.\n",
|
|
contextp->Config.InterfaceIndex);
|
|
if((err = RmActivateRoute(contextp->hPort, &contextp->Config)) == NO_ERROR) {
|
|
|
|
contextp->RouteState = ROUTE_ACTIVATED;
|
|
contextp->IpxwanState = IPXWAN_ACTIVE;
|
|
|
|
err = PENDING;
|
|
}
|
|
|
|
break;
|
|
|
|
case IPXWAN_ACTIVE:
|
|
err = PENDING;
|
|
break;
|
|
|
|
case IPXWAN_DONE:
|
|
default:
|
|
err = contextp->IpxwanConfigResult;
|
|
break;
|
|
}
|
|
|
|
RELEASE_DATABASE_LOCK;
|
|
|
|
return err;
|
|
}
|
|
|
|
//
|
|
//*** Configuration done with IPXCP ***
|
|
//
|
|
|
|
if(contextp->RouteState != ROUTE_ALLOCATED) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// call LineUp indication into the IPX stack with the negociated config
|
|
// values.
|
|
TraceIpx(
|
|
PPPIF_TRACE,
|
|
"IpxCpThisLayerUp: Config complete, Do LINEUP on if #%d.\n",
|
|
contextp->Config.InterfaceIndex);
|
|
if(err = RmActivateRoute(contextp->hPort, &contextp->Config)) {
|
|
return err;
|
|
}
|
|
|
|
TraceIpx(PPPIF_TRACE,"\n*** IPXCP final configuration ***\n");
|
|
TraceIpx(PPPIF_TRACE," Network: %.2x%.2x%.2x%.2x\n",
|
|
contextp->Config.Network[0],
|
|
contextp->Config.Network[1],
|
|
contextp->Config.Network[2],
|
|
contextp->Config.Network[3]);
|
|
|
|
TraceIpx(PPPIF_TRACE," LocalNode: %.2x%.2x%.2x%.2x%.2x%.2x\n",
|
|
contextp->Config.LocalNode[0],
|
|
contextp->Config.LocalNode[1],
|
|
contextp->Config.LocalNode[2],
|
|
contextp->Config.LocalNode[3],
|
|
contextp->Config.LocalNode[4],
|
|
contextp->Config.LocalNode[5]);
|
|
|
|
TraceIpx(PPPIF_TRACE," RemoteNode: %.2x%.2x%.2x%.2x%.2x%.2x\n",
|
|
contextp->Config.RemoteNode[0],
|
|
contextp->Config.RemoteNode[1],
|
|
contextp->Config.RemoteNode[2],
|
|
contextp->Config.RemoteNode[3],
|
|
contextp->Config.RemoteNode[4],
|
|
contextp->Config.RemoteNode[5]);
|
|
|
|
TraceIpx(PPPIF_TRACE," ReceiveCompression = %d SendCompression = %d\n",
|
|
contextp->SetReceiveCompressionProtocol,
|
|
contextp->SetSendCompressionProtocol);
|
|
|
|
contextp->RouteState = ROUTE_ACTIVATED;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//***
|
|
//
|
|
// Function: IpxMakeConfigRequest
|
|
//
|
|
// Descr: Builds the config request packet from the desired parameters
|
|
//
|
|
//***
|
|
|
|
DWORD
|
|
IpxCpMakeConfigRequest(IN VOID *pWorkBuffer,
|
|
OUT PPP_CONFIG *pRequestBuffer,
|
|
IN DWORD cbRequestBuffer)
|
|
{
|
|
USHORT cnfglen;
|
|
PUCHAR cnfgptr;
|
|
USHORT optlen;
|
|
PIPXCP_CONTEXT contextp;
|
|
int i;
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigRequest: Entered for if # %d\n",
|
|
contextp->Config.InterfaceIndex);
|
|
|
|
if(contextp->RouteState == NO_ROUTE) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxcpMakeConfigRequest: No route allocated!\n");
|
|
return(ERROR_NO_NETWORK);
|
|
}
|
|
|
|
// check that the request buffer is big enough to get the desired
|
|
// parameters
|
|
if((USHORT)cbRequestBuffer < DesiredConfigReqLength()) {
|
|
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
pRequestBuffer->Code = CONFIG_REQ;
|
|
|
|
cnfglen = 4;
|
|
cnfgptr = (PUCHAR)pRequestBuffer;
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
// Do not request any option
|
|
PUTUSHORT2SHORT(pRequestBuffer->Length, cnfglen);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// set the desired options
|
|
for(i = 0; i < MaxDesiredParameters; i++) {
|
|
|
|
if(!contextp->DesiredParameterNegotiable[i]) {
|
|
|
|
// do not request this config option
|
|
continue;
|
|
}
|
|
|
|
OptionHandler[DesiredParameter[i]](cnfgptr + cnfglen,
|
|
contextp,
|
|
NULL,
|
|
SNDREQ_OPTION);
|
|
|
|
optlen = *(cnfgptr + cnfglen + OPTIONH_LENGTH);
|
|
cnfglen += optlen;
|
|
}
|
|
|
|
// set the length of the configuration request frame
|
|
PUTUSHORT2SHORT(pRequestBuffer->Length, cnfglen);
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//***
|
|
//
|
|
// Function: IpxMakeConfigResult
|
|
//
|
|
// Descr: Starts by building the ack packet as the result
|
|
// If an option gets NAKed (!), it resets the result packet
|
|
// and starts building the NAK packet instead.
|
|
// If an option gets rejected, only the reject packet will
|
|
// be built.
|
|
// If one of the desired parameters is missing from this
|
|
// configuration request, we reset the result packet and start
|
|
// building a NAK packet with the missing desired parameters.
|
|
//
|
|
//***
|
|
|
|
#define MAX_OPTION_LENGTH 512
|
|
|
|
DWORD
|
|
IpxCpMakeConfigResult(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer,
|
|
OUT PPP_CONFIG *pResultBuffer,
|
|
IN DWORD cbResultBuffer,
|
|
IN BOOL fRejectNaks)
|
|
{
|
|
USHORT cnfglen; // config request packet len
|
|
USHORT rcvlen; // used to scan the received options packet
|
|
USHORT reslen; // result length
|
|
PUCHAR rcvptr;
|
|
PUCHAR resptr; // result ptr
|
|
PIPXCP_CONTEXT contextp;
|
|
UCHAR option; // value of this option
|
|
UCHAR optlen; // length of this option
|
|
BOOL DesiredParameterRequested[MAX_DESIRED_PARAMETERS];
|
|
USHORT i;
|
|
BOOL AllDesiredParamsRequested;
|
|
UCHAR nakedoption[MAX_OPTION_LENGTH];
|
|
DWORD rc;
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: Entered for if # %d\n",
|
|
contextp->Config.InterfaceIndex);
|
|
|
|
if(contextp->RouteState == NO_ROUTE) {
|
|
|
|
return(ERROR_NO_NETWORK);
|
|
}
|
|
|
|
// start by marking all negotiable parameters as not requested yet
|
|
for(i=0; i<MaxDesiredParameters; i++) {
|
|
|
|
DesiredParameterRequested[i] = !contextp->DesiredParameterNegotiable[i];
|
|
}
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
// get the total cnfg request packet length
|
|
GETSHORT2USHORT(&cnfglen, pReceiveBuffer->Length);
|
|
|
|
// check that the result buffer is at least as big as the receive buffer
|
|
if((USHORT)cbResultBuffer < cnfglen) {
|
|
|
|
return(ERROR_PPP_INVALID_PACKET);
|
|
}
|
|
|
|
// set the ptrs and length to the start of the options in the packet
|
|
pResultBuffer->Code = CONFIG_ACK;
|
|
rcvptr = (PUCHAR)pReceiveBuffer;
|
|
resptr = (PUCHAR)pResultBuffer;
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
if(cnfglen > 4) {
|
|
|
|
pResultBuffer->Code = CONFIG_REJ;
|
|
|
|
for(rcvlen = reslen = 4;
|
|
rcvlen < cnfglen;
|
|
rcvlen += optlen) {
|
|
|
|
// get the current option type and length
|
|
option = *(rcvptr + rcvlen + OPTIONH_TYPE);
|
|
optlen = *(rcvptr + rcvlen + OPTIONH_LENGTH);
|
|
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
}
|
|
|
|
// set the final result length
|
|
PUTUSHORT2SHORT(pResultBuffer->Length, reslen);
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: reject all options because IPXWAN required\n");
|
|
}
|
|
else
|
|
{
|
|
PUTUSHORT2SHORT(pResultBuffer->Length, 4);
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: ack null options packet because IPXWAN required\n");
|
|
}
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
for(rcvlen = reslen = 4;
|
|
rcvlen < cnfglen;
|
|
rcvlen += optlen) {
|
|
|
|
// get the current option type and length
|
|
option = *(rcvptr + rcvlen + OPTIONH_TYPE);
|
|
optlen = *(rcvptr + rcvlen + OPTIONH_LENGTH);
|
|
|
|
switch(pResultBuffer->Code) {
|
|
|
|
case CONFIG_ACK:
|
|
|
|
// Check if this is a valid option
|
|
if((rc = ValidOption(option, optlen)) != NO_ERROR) {
|
|
|
|
if(rc == ERROR_INVALID_OPTLEN) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: option %d has invalid length %d\n",
|
|
option, optlen);
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
// reject this option
|
|
pResultBuffer->Code = CONFIG_REJ;
|
|
|
|
// restart the result packet with this rejected option
|
|
reslen = 4;
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: REJECT option %\n",
|
|
option);
|
|
|
|
break;
|
|
}
|
|
|
|
// Option is valid.
|
|
// Check if it is desired and acceptable
|
|
if(DesiredOption(option, &i)) {
|
|
|
|
DesiredParameterRequested[i] = TRUE;
|
|
|
|
if(!OptionHandler[option](rcvptr + rcvlen,
|
|
contextp,
|
|
nakedoption,
|
|
RCVREQ_OPTION)) {
|
|
|
|
|
|
// if this is a renegociation, we are not converging!
|
|
if(contextp->RouteState == ROUTE_ACTIVATED) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: Not Converging\n");
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
if((option == IPX_NETWORK_NUMBER) &&
|
|
(contextp->NetNumberNakSentCount >= MAX_NET_NUMBER_NAKS_SENT)) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: Not converging because TOO MANY NAKs SENT!!\n");
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
//
|
|
//*** NAK this option ***
|
|
//
|
|
|
|
// check if we should send a reject instead
|
|
if(fRejectNaks) {
|
|
|
|
// make up a reject packet
|
|
pResultBuffer->Code = CONFIG_REJ;
|
|
|
|
// restart the result packet with this rejected option
|
|
reslen = 4;
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
|
|
break;
|
|
}
|
|
|
|
pResultBuffer->Code = CONFIG_NAK;
|
|
|
|
// restart the result packet with the NAK-ed option
|
|
reslen = 4;
|
|
CopyOption(resptr + reslen, nakedoption);
|
|
|
|
reslen += optlen;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: NAK option %d\n", option);
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
// Option is valid and either desired AND accepted or
|
|
// not desired and we will accept it without any testing
|
|
// Ack it and increment the result length.
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
|
|
break;
|
|
|
|
case CONFIG_NAK:
|
|
|
|
// Check if this is a valid option
|
|
if((rc = ValidOption(*(rcvptr + rcvlen + OPTIONH_TYPE),
|
|
*(rcvptr + rcvlen + OPTIONH_LENGTH))) != NO_ERROR) {
|
|
|
|
if(rc == ERROR_INVALID_OPTLEN) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: option %d has invalid length %d\n",
|
|
*(rcvptr + rcvlen + OPTIONH_TYPE),
|
|
*(rcvptr + rcvlen + OPTIONH_LENGTH));
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
// reject this option
|
|
pResultBuffer->Code = CONFIG_REJ;
|
|
|
|
// restart the result packet with this rejected option
|
|
reslen = 4;
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
|
|
break;
|
|
}
|
|
|
|
// We are looking only for options to NAK and skip all others
|
|
if(DesiredOption(option, &i)) {
|
|
|
|
DesiredParameterRequested[i] = TRUE;
|
|
|
|
if(!OptionHandler[option](rcvptr + rcvlen,
|
|
contextp,
|
|
resptr + reslen,
|
|
RCVREQ_OPTION)) {
|
|
reslen += optlen;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: NAK option %d\n", option);
|
|
|
|
if((option == IPX_NETWORK_NUMBER) &&
|
|
(contextp->NetNumberNakSentCount >= MAX_NET_NUMBER_NAKS_SENT)) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: TOO MANY NAKs SENT!!\n");
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case CONFIG_REJ:
|
|
|
|
// We are looking only for options to reject and skip all others
|
|
if((rc = ValidOption(*(rcvptr + rcvlen + OPTIONH_TYPE),
|
|
*(rcvptr + rcvlen + OPTIONH_LENGTH))) != NO_ERROR) {
|
|
|
|
if(rc == ERROR_INVALID_OPTLEN) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpMakeConfigResult: option %d has invalid length %d\n",
|
|
*(rcvptr + rcvlen + OPTIONH_TYPE),
|
|
*(rcvptr + rcvlen + OPTIONH_LENGTH));
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
CopyOption(resptr + reslen, rcvptr + rcvlen);
|
|
reslen += optlen;
|
|
}
|
|
|
|
if(DesiredOption(option, &i)) {
|
|
|
|
DesiredParameterRequested[i] = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
SS_ASSERT(FALSE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check if all our desired parameters have been requested
|
|
AllDesiredParamsRequested = TRUE;
|
|
|
|
for(i=0; i<MaxDesiredParameters; i++) {
|
|
|
|
if(!DesiredParameterRequested[i]) {
|
|
|
|
AllDesiredParamsRequested = FALSE;
|
|
}
|
|
}
|
|
|
|
if(AllDesiredParamsRequested) {
|
|
|
|
//
|
|
//*** ALL DESIRED PARAMETERS HAVE BEEN REQUESTED ***
|
|
//
|
|
|
|
// set the final result length
|
|
PUTUSHORT2SHORT(pResultBuffer->Length, reslen);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
//
|
|
//*** SOME DESIRED PARAMETERS ARE MISSING ***
|
|
//
|
|
|
|
// check that we have enough result buffer to transmit all our non received
|
|
// desired params
|
|
if((USHORT)cbResultBuffer < DesiredConfigReqLength()) {
|
|
|
|
return(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
|
|
switch(pResultBuffer->Code) {
|
|
|
|
case CONFIG_ACK:
|
|
|
|
// the only case where we request a NAK when a requested parameter
|
|
// is missing is when this parameter is the node number
|
|
if(DesiredParameterRequested[IPX_NODE_NUMBER_INDEX]) {
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// reset the ACK packet and make it NAK packet
|
|
pResultBuffer->Code = CONFIG_NAK;
|
|
reslen = 4;
|
|
|
|
// FALL THROUGH
|
|
}
|
|
|
|
case CONFIG_NAK:
|
|
|
|
// Append the missing options in the NAK packet
|
|
for(i=0; i<MaxDesiredParameters; i++) {
|
|
|
|
if(DesiredParameterRequested[i]) {
|
|
|
|
// skip it!
|
|
continue;
|
|
}
|
|
|
|
option = DesiredParameter[i];
|
|
|
|
if((option == IPX_NETWORK_NUMBER) ||
|
|
(option == IPX_COMPRESSION_PROTOCOL)) {
|
|
|
|
// These two desired options are not forced in a nak if
|
|
// the other end didn't provide them.
|
|
|
|
// skip it!
|
|
continue;
|
|
}
|
|
|
|
OptionHandler[option](NULL,
|
|
contextp,
|
|
resptr + reslen,
|
|
SNDNAK_OPTION);
|
|
|
|
optlen = *(resptr + reslen + OPTIONH_LENGTH);
|
|
reslen += optlen;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
// set the final result length
|
|
PUTUSHORT2SHORT(pResultBuffer->Length, reslen);
|
|
|
|
return (NO_ERROR);
|
|
}
|
|
|
|
DWORD
|
|
IpxCpConfigNakReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
PUCHAR rcvptr;
|
|
USHORT rcvlen;
|
|
USHORT naklen;
|
|
UCHAR option;
|
|
UCHAR optlen;
|
|
DWORD rc;
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigNakReceived: Entered for if # %d\n",
|
|
contextp->Config.InterfaceIndex);
|
|
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
rcvptr = (PUCHAR)pReceiveBuffer;
|
|
GETSHORT2USHORT(&naklen, pReceiveBuffer->Length);
|
|
|
|
for(rcvlen = 4; rcvlen < naklen; rcvlen += optlen) {
|
|
|
|
// get the current option type and length
|
|
option = *(rcvptr + rcvlen + OPTIONH_TYPE);
|
|
optlen = *(rcvptr + rcvlen + OPTIONH_LENGTH);
|
|
|
|
if((rc = ValidOption(option, optlen)) != NO_ERROR) {
|
|
|
|
if(rc == ERROR_INVALID_OPTLEN) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigNakReceived: option %d has invalid length %d\n",
|
|
option, optlen);
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigNakReceived: option %d not valid\n", option);
|
|
|
|
// ignore this option
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// valid option
|
|
OptionHandler[option](rcvptr + rcvlen,
|
|
contextp,
|
|
NULL,
|
|
RCVNAK_OPTION);
|
|
|
|
if((option == IPX_NETWORK_NUMBER) &&
|
|
(contextp->NetNumberNakReceivedCount >= MAX_NET_NUMBER_NAKS_RECEIVED)) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigNakReceived: TOO MANY NAKs RECEIVED !! terminate IPXCP negotiation\n");
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
IpxCpConfigAckReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
PUCHAR rcvptr;
|
|
USHORT rcvlen;
|
|
USHORT acklen;
|
|
UCHAR option;
|
|
USHORT optlen;
|
|
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigAckReceived: Entered for if # %d\n",
|
|
contextp->Config.InterfaceIndex);
|
|
|
|
// check that this is what we have requested
|
|
rcvptr = (PUCHAR)pReceiveBuffer;
|
|
GETSHORT2USHORT(&acklen, pReceiveBuffer->Length);
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
if(acklen != 4) {
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
else
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
for(rcvlen = 4; rcvlen < acklen; rcvlen += optlen) {
|
|
|
|
// get the current option type and length
|
|
option = *(rcvptr + rcvlen + OPTIONH_TYPE);
|
|
optlen = *(rcvptr + rcvlen + OPTIONH_LENGTH);
|
|
|
|
if(!DesiredOption(option, NULL)) {
|
|
|
|
// this is not our option!
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigAckReceived: Option %d not desired\n", option);
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
if(!OptionHandler[option](rcvptr + rcvlen,
|
|
contextp,
|
|
NULL,
|
|
RCVACK_OPTION)) {
|
|
|
|
// this option doesn't have our configured request value
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigAckReceived: Option %d not our value\n", option);
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
}
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigAckReceived: All options validated\n");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
DWORD
|
|
IpxCpConfigRejReceived(IN VOID *pWorkBuffer,
|
|
IN PPP_CONFIG *pReceiveBuffer)
|
|
{
|
|
PIPXCP_CONTEXT contextp;
|
|
PUCHAR rcvptr;
|
|
USHORT rcvlen;
|
|
USHORT rejlen;
|
|
UCHAR option;
|
|
USHORT optlen;
|
|
int i;
|
|
|
|
// if we are a server node or a client on a server machine, we don't accept
|
|
// any rejection
|
|
if(IsRouterStarted()) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigRejReceived: Cannot handle rejects on a router, aborting\n");
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
// This node doesn't have a router. We continue the negotiation with the
|
|
// remaining options.
|
|
// If the network number negotiation has been rejected, we will tell the
|
|
// ipx stack that we have net number 0 and let it deal with it.
|
|
|
|
contextp = (PIPXCP_CONTEXT)pWorkBuffer;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigRejReceived: Entered for if # %d\n",
|
|
contextp->Config.InterfaceIndex);
|
|
|
|
if(contextp->Config.IpxwanConfigRequired) {
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
// check that this is what we have requested
|
|
rcvptr = (PUCHAR)pReceiveBuffer;
|
|
GETSHORT2USHORT(&rejlen, pReceiveBuffer->Length);
|
|
|
|
for(rcvlen = 4; rcvlen < rejlen; rcvlen += optlen) {
|
|
|
|
// get the current option type and length
|
|
option = *(rcvptr + rcvlen + OPTIONH_TYPE);
|
|
optlen = *(rcvptr + rcvlen + OPTIONH_LENGTH);
|
|
|
|
if(optlen == 0) {
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigRejReceived: received null option length, aborting\n");
|
|
|
|
return ERROR_PPP_NOT_CONVERGING;
|
|
}
|
|
|
|
for(i=0; i<MAX_DESIRED_PARAMETERS; i++) {
|
|
|
|
if(option == DesiredParameter[i]) {
|
|
|
|
switch(i) {
|
|
|
|
case 0:
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigRejReceived: Turn off Network Number negotiation\n");
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpConfigRejReceived: Turn off Node Number negotiation\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
}
|
|
|
|
contextp->DesiredParameterNegotiable[i] = FALSE;
|
|
|
|
// if this is the node configuration rejected, set the remote
|
|
// node to 0 to indicate it's unknown.
|
|
if(option == IPX_NODE_NUMBER) {
|
|
|
|
memcpy(contextp->Config.RemoteNode, nullnode, 6);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
IpxCpThisLayerDown(IN VOID *pWorkBuffer)
|
|
{
|
|
dwClientCount--;
|
|
TraceIpx(PPPIF_TRACE, "IpxCpThisLayerDown: Entered (%d total)\n", dwClientCount);
|
|
|
|
// If the last client hung up, go ahead and update all of the global
|
|
// config
|
|
if (dwClientCount == 0)
|
|
IpxcpUpdateQueuedGlobalConfig();
|
|
|
|
return 0L;
|
|
}
|
|
|
|
char * YesNo (DWORD dwVal) {
|
|
return (dwVal) ? "YES" : "NO";
|
|
}
|
|
|
|
//
|
|
// Gets called when a registry value related to ppp changes. This
|
|
// function must read in the new registry config and plumb any
|
|
// changes it finds.
|
|
//
|
|
// The following values will be supported for on the fly update
|
|
// since they are exposed through connections ui:
|
|
// CQC_THIS_MACHINE_ONLY
|
|
// CQC_ENABLE_GLOBAL_WAN_NET
|
|
// CQC_FIRST_WAN_NET
|
|
// CQC_ENABLE_AUTO_WAN_NET_ALLOCATION
|
|
// CQC_ACCEPT_REMOTE_NODE_NUMBER
|
|
//
|
|
// In addition the following will be supported
|
|
// CQC_FIRST_WAN_NODE
|
|
//
|
|
DWORD
|
|
IpxCpUpdateGlobalConfig()
|
|
{
|
|
IPXCP_GLOBAL_CONFIG_PARAMS Params;
|
|
DWORD dwErr;
|
|
INT iCmp;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpUpdateGlobalConfig: Entered");
|
|
|
|
// Re-read the configuration from the registry
|
|
CopyMemory (&Params, &GlobalConfig, sizeof (Params));
|
|
GetIpxCpParameters(&Params);
|
|
|
|
// First, go through and update any parameters that can immediately be
|
|
// applied. Included in these are: CQC_THIS_MACHINE_ONLY
|
|
//
|
|
if (!!(Params.RParams.ThisMachineOnly) != !!(GlobalConfig.RParams.ThisMachineOnly)) {
|
|
GlobalConfig.RParams.ThisMachineOnly = !!(Params.RParams.ThisMachineOnly);
|
|
// Tell the router manager that some configuration has updated
|
|
if (RmUpdateIpxcpConfig) {
|
|
if ((dwErr = RmUpdateIpxcpConfig (&(GlobalConfig.RParams))) != NO_ERROR)
|
|
return dwErr;
|
|
}
|
|
}
|
|
|
|
// Now, go through and queue any settings that will take
|
|
// delayed effect.
|
|
//
|
|
// Queue any changes to the global wan net enabling
|
|
if (!!(Params.RParams.EnableGlobalWanNet) != !!(GlobalConfig.RParams.EnableGlobalWanNet))
|
|
CQAdd ( hConfigQueue,
|
|
CQC_ENABLE_GLOBAL_WAN_NET,
|
|
&(Params.RParams.EnableGlobalWanNet),
|
|
sizeof (Params.RParams.EnableGlobalWanNet));
|
|
|
|
// Queue any changes to the globalwan/firstwan setting
|
|
if (Params.FirstWanNet != GlobalConfig.FirstWanNet)
|
|
CQAdd (hConfigQueue, CQC_FIRST_WAN_NET, &(Params.FirstWanNet), sizeof(Params.FirstWanNet));
|
|
|
|
// Queue any changes to the auto net assignment enabling
|
|
if (!!(Params.EnableAutoWanNetAllocation) != !!(GlobalConfig.EnableAutoWanNetAllocation))
|
|
CQAdd ( hConfigQueue,
|
|
CQC_ENABLE_AUTO_WAN_NET_ALLOCATION,
|
|
&(Params.EnableAutoWanNetAllocation),
|
|
sizeof (Params.EnableAutoWanNetAllocation));
|
|
|
|
// Queue any changes to the accept remote node enabling
|
|
if (!!(Params.AcceptRemoteNodeNumber) != !!(GlobalConfig.AcceptRemoteNodeNumber))
|
|
CQAdd ( hConfigQueue,
|
|
CQC_ACCEPT_REMOTE_NODE_NUMBER,
|
|
&(Params.AcceptRemoteNodeNumber),
|
|
sizeof (Params.AcceptRemoteNodeNumber));
|
|
|
|
// Queue any changes to the first remote node setting
|
|
if (memcmp (Params.puSpecificNode, GlobalConfig.puSpecificNode, 6) != 0)
|
|
CQAdd (hConfigQueue, CQC_FIRST_WAN_NODE, Params.puSpecificNode, 6);
|
|
|
|
// If there are no clients, go ahead and update the queued config
|
|
//
|
|
if (dwClientCount == 0) {
|
|
if ((dwErr = IpxcpUpdateQueuedGlobalConfig()) != NO_ERROR)
|
|
return dwErr;
|
|
}
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxCpUpdateGlobalConfig: exiting...\n");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Callback function used to update each piece of queued configuration
|
|
// data one at a time. The client count is assumed to be zero when this
|
|
// function is called.
|
|
//
|
|
BOOL
|
|
IpxcpUpdateConfigItem (DWORD dwCode, LPVOID pvData, DWORD dwSize, ULONG_PTR ulpUser) {
|
|
DWORD dwErr, dwData = *(DWORD*)pvData;
|
|
PUCHAR puData = (PUCHAR)pvData;
|
|
DWORD* pdwFlags = (DWORD*)ulpUser;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: Entered for item code %x", dwCode);
|
|
|
|
switch (dwCode) {
|
|
case CQC_ENABLE_GLOBAL_WAN_NET:
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: EnableGlobalWanNet %s", YesNo(dwData));
|
|
GlobalConfig.RParams.EnableGlobalWanNet = !!dwData;
|
|
*pdwFlags |= FLAG_UPDATE_ROUTER | FLAG_UPDATE_WANNET;
|
|
break;
|
|
|
|
case CQC_FIRST_WAN_NET:
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: FirstWanNet %x", dwData);
|
|
GlobalConfig.FirstWanNet = dwData;
|
|
*pdwFlags |= FLAG_UPDATE_WANNET;
|
|
break;
|
|
|
|
case CQC_ENABLE_AUTO_WAN_NET_ALLOCATION:
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: EnableAutoAssign %s", YesNo(dwData));
|
|
GlobalConfig.EnableAutoWanNetAllocation = !!dwData;
|
|
*pdwFlags |= FLAG_UPDATE_WANNET;
|
|
break;
|
|
|
|
case CQC_ACCEPT_REMOTE_NODE_NUMBER:
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: AcceptRemoteNodeNumber %s", YesNo(dwData));
|
|
GlobalConfig.AcceptRemoteNodeNumber = !!dwData;
|
|
break;
|
|
|
|
case CQC_FIRST_WAN_NODE:
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateConfigItem: FirstWanNode %x%x%x%x%x%x", puData[0], puData[1], puData[2], puData[3], puData[4], puData[5]);
|
|
memcpy (GlobalConfig.puSpecificNode, pvData, 6);
|
|
GETLONG2ULONG(&LastNodeAssigned,&(GlobalConfig.puSpecificNode[2]));
|
|
break;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Called only when client count is zero to update any settings that have
|
|
// been queued to take place.
|
|
//
|
|
DWORD
|
|
IpxcpUpdateQueuedGlobalConfig() {
|
|
DWORD dwErr, dwFlags = 0;
|
|
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateQueuedGlobalConfig: entered");
|
|
|
|
// Enumerate all of the queued config information updating
|
|
// global config as we go.
|
|
//
|
|
if ((dwErr = CQEnum (hConfigQueue, IpxcpUpdateConfigItem, (ULONG_PTR)&dwFlags)) != NO_ERROR)
|
|
return dwErr;
|
|
|
|
// If we need to update the wan net, do so
|
|
//
|
|
if (dwFlags & FLAG_UPDATE_WANNET) {
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateQueuedGlobalConfig: Updating WanNet Information.");
|
|
WanNetReconfigure();
|
|
}
|
|
|
|
// If we need to update the router, do so
|
|
//
|
|
if (dwFlags & FLAG_UPDATE_ROUTER) {
|
|
TraceIpx(PPPIF_TRACE, "IpxcpUpdateQueuedGlobalConfig: Updating Router.");
|
|
if (RmUpdateIpxcpConfig) {
|
|
if ((dwErr = RmUpdateIpxcpConfig (&(GlobalConfig.RParams))) != NO_ERROR)
|
|
return dwErr;
|
|
}
|
|
}
|
|
|
|
// Clear the queued config data as it has been completely processed
|
|
// at this point.
|
|
//
|
|
CQRemoveAll (hConfigQueue);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ValidOption(UCHAR option,
|
|
UCHAR optlen)
|
|
{
|
|
//
|
|
// NarenG: Put in fix for Bug # 74555. Otherwise PPP was blocked in
|
|
// NakReceived which was in a forever loop.
|
|
//
|
|
|
|
if ( optlen == 0 )
|
|
{
|
|
return( ERROR_INVALID_OPTLEN );
|
|
}
|
|
|
|
switch(option)
|
|
{
|
|
case IPX_NETWORK_NUMBER:
|
|
if(optlen != 6)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
return NO_ERROR;
|
|
break;
|
|
|
|
case IPX_NODE_NUMBER:
|
|
if(optlen != 8)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
return NO_ERROR;
|
|
break;
|
|
|
|
case IPX_ROUTING_PROTOCOL:
|
|
if(optlen < 4)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
return NO_ERROR;
|
|
break;
|
|
|
|
case IPX_ROUTER_NAME:
|
|
if(optlen < 3)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
else
|
|
{
|
|
TraceIpx(
|
|
PPPIF_TRACE,
|
|
"ValidOption: Accept Router Name w/ valid len.");
|
|
|
|
return NO_ERROR;
|
|
}
|
|
break;
|
|
|
|
case IPX_CONFIGURATION_COMPLETE:
|
|
if(optlen != 2)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
return NO_ERROR;
|
|
break;
|
|
|
|
case IPX_COMPRESSION_PROTOCOL:
|
|
if(GlobalConfig.EnableCompressionProtocol)
|
|
{
|
|
if(optlen < 4)
|
|
{
|
|
return ERROR_INVALID_OPTLEN;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return ERROR_INVALID_OPTION;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return ERROR_INVALID_OPTION;
|
|
}
|
|
|
|
return ERROR_INVALID_OPTION;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DesiredOption(UCHAR option, USHORT *indexp)
|
|
{
|
|
USHORT i;
|
|
|
|
for(i=0; i<MaxDesiredParameters; i++) {
|
|
|
|
if(option == DesiredParameter[i]) {
|
|
|
|
if(indexp) {
|
|
|
|
*indexp = i;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
USHORT
|
|
DesiredConfigReqLength(VOID)
|
|
{
|
|
USHORT i, len;
|
|
|
|
for(i=0, len=0; i<MaxDesiredParameters; i++) {
|
|
|
|
len += DesiredParameterLength[i];
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
|
|
|
|
|