|
|
/********************************************************************/ /** Copyright(c) 1989 Microsoft Corporation. **/ /********************************************************************/
//***
//
// Filename: smevents.c
//
// Description: This module contain the events processing code for the
// Finite State Machine for PPP.
//
// History:
// Oct 25,1993. NarenG Created Original version.
//
//
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h> // needed for winbase.h
#include <windows.h> // Win32 base API's
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <rasauth.h>
#include <raseapif.h>
#include <lmcons.h>
#include <raserror.h>
#include <rasman.h>
#include <rtutils.h>
#include <rasppp.h>
#include <pppcp.h>
#include <ppp.h>
#include <smaction.h>
#include <smevents.h>
#include <receive.h>
#include <auth.h>
#include <callback.h>
#include <lcp.h>
#include <timer.h>
#include <util.h>
#include <worker.h>
#include <bap.h>
static VOID (*ProcessPacket[])( PCB * pPcb, DWORD CpIndex, CPCB * pCpCb, PPP_CONFIG * pRecvConfig ) = { NULL, ReceiveConfigReq, ReceiveConfigAck, ReceiveConfigNakRej, ReceiveConfigNakRej, ReceiveTermReq, ReceiveTermAck, ReceiveCodeRej, NULL, ReceiveEchoReq, ReceiveEchoReply, ReceiveDiscardReq, ReceiveIdentification, ReceiveTimeRemaining };
/************************************************************************/ /* E V E N T P R O C E S S I N G */ /************************************************************************/
//**
//
// Call: FsmUp
//
// Returns: none.
//
// Description: This is called after a Line Up event occurs.
//
VOID FsmUp( IN PCB * pPcb, IN DWORD CpIndex ) { CPCB * pCpCb = GetPointerToCPCB( pPcb, CpIndex );
if ( pCpCb == NULL ) { return; }
PppLog( 2, "FsmUp event received for protocol %x on port %d", CpTable[CpIndex].CpInfo.Protocol, pPcb->hPort );
if ( CpIndex == LCP_INDEX ) { pPcb->PppPhase = PPP_LCP; }
switch( pCpCb->State ) {
case FSM_INITIAL:
pCpCb->State = FSM_CLOSED;
break;
case FSM_STARTING:
InitRestartCounters( pPcb, pCpCb );
if ( !FsmSendConfigReq( pPcb, CpIndex, FALSE ) ) { //
// If we couldn't even send the first configure request, we mark
// this protocol as NOT CONFIGURABLE so that we protocol reject
// this layer. We need to do this since FsmClose will not send
// a terminate request in this state (as per the PPP FSM) and we
// want to terminate this layer gracefully instead of simply
// dropping all the clients packets and having the client
// timeout.
//
pCpCb->fConfigurable = FALSE;
return; }
pCpCb->State = FSM_REQ_SENT;
break;
default:
//
// Already started
//
PPP_ASSERT( pCpCb->State < 10 );
PppLog( 2, "Illegal transition -> FsmUp received while in %s state", FsmStates[pCpCb->State] );
break; } }
//**
//
// Call: FsmOpen
//
// Returns: None.
//
// Description: This is called after an Open event occurs.
//
VOID FsmOpen( IN PCB * pPcb, IN DWORD CpIndex ) { CPCB * pCpCb = GetPointerToCPCB( pPcb, CpIndex );
if ( pCpCb == NULL ) { return; }
PppLog( 2, "FsmOpen event received for protocol %x on port %d", CpTable[CpIndex].CpInfo.Protocol, pPcb->hPort );
switch( pCpCb->State ) {
case FSM_INITIAL:
if ( !FsmThisLayerStarted( pPcb, CpIndex ) ) return;
pCpCb->State = FSM_STARTING;
break;
case FSM_STARTING: case FSM_REQ_SENT: case FSM_ACK_RCVD: case FSM_ACK_SENT:
break;
case FSM_CLOSING:
pCpCb->State = FSM_STOPPING;
//
// Fallthru
//
case FSM_OPENED: case FSM_STOPPED: case FSM_STOPPING:
//
// Restart option not implemented.
//
// FsmDown( pPcb, CpIndex );
// FsmUp( pPcb, CpIndex );
//
break;
case FSM_CLOSED:
InitRestartCounters( pPcb, pCpCb );
if ( !FsmSendConfigReq( pPcb, CpIndex, FALSE ) ) return;
pCpCb->State = FSM_REQ_SENT;
break;
default:
PPP_ASSERT( pCpCb->State < 10 );
PppLog( 2, "Illegal transition->FsmOpen received while in %s state", FsmStates[pCpCb->State] ); break; } }
//**
//
// Call: FsmDown
//
// Returns: None.
//
// Description: Will get called after the physical line goes down.
//
VOID FsmDown( IN PCB * pPcb, IN DWORD CpIndex ) { CPCB * pCpCb = GetPointerToCPCB( pPcb, CpIndex );
if ( pCpCb == NULL ) { return; }
PppLog( 2, "FsmDown event received for protocol %x on port %d", CpTable[CpIndex].CpInfo.Protocol, pPcb->hPort );
RemoveFromTimerQ( pPcb->dwPortId, pCpCb->LastId, CpTable[CpIndex].CpInfo.Protocol, FALSE, TIMER_EVENT_TIMEOUT );
switch( pCpCb->State ) {
case FSM_CLOSED: case FSM_CLOSING:
if ( !FsmReset( pPcb, CpIndex ) ) return;
pCpCb->State = FSM_INITIAL;
break;
case FSM_OPENED:
if ( !FsmThisLayerDown( pPcb, CpIndex ) ) return;
//
// Fallthru
//
case FSM_REQ_SENT: case FSM_ACK_RCVD: case FSM_ACK_SENT: case FSM_STOPPING:
if ( !FsmReset( pPcb, CpIndex ) ) return;
pCpCb->State = FSM_STARTING;
break;
case FSM_STOPPED:
if ( !FsmThisLayerStarted( pPcb, CpIndex ) ) return;
if ( !FsmReset( pPcb, CpIndex ) ) return;
pCpCb->State = FSM_STARTING;
break;
case FSM_STARTING: case FSM_INITIAL: default:
PPP_ASSERT( pCpCb->State < 10 );
PppLog( 2, "Illegal transition->FsmDown received while in %s state", FsmStates[pCpCb->State] ); break; }
if ( CpIndex == LCP_INDEX ) { pPcb->PppPhase = PPP_LCP; } }
//**
//
// Call: FsmClose
//
// Returns: None.
//
// Description: Will get called when a close connection is requested.
// NOTE: Call FsmThisLayerFinished in the states where we do
// not have to send a Term Req. and wait for a Term Ack.
// This is done so that it is guaranteed that
// FsmThisLayerFinished is called in ALL states. We need
// to do this since all processing of failures is done in
// the FsmThisLayerFinished call.
//
VOID FsmClose( IN PCB * pPcb, IN DWORD CpIndex ) { CPCB * pCpCb = GetPointerToCPCB( pPcb, CpIndex );
if ( pCpCb == NULL ) { return; }
PppLog( 2, "FsmClose event received for protocol %x on port %d", CpTable[CpIndex].CpInfo.Protocol, pPcb->hPort );
if ( CpIndex == LCP_INDEX ) { pPcb->PppPhase = PPP_LCP; }
//
// We are closing this layer so remove any items from the timer Q
//
RemoveFromTimerQ( pPcb->dwPortId, pCpCb->LastId, CpTable[CpIndex].CpInfo.Protocol, FALSE, TIMER_EVENT_TIMEOUT );
switch ( pCpCb->State ) {
case FSM_STARTING:
pCpCb->State = FSM_INITIAL;
if ( !FsmThisLayerFinished( pPcb, CpIndex, FALSE ) ) return;
break;
case FSM_STOPPED:
pCpCb->State = FSM_CLOSED;
if ( !FsmThisLayerFinished( pPcb, CpIndex, FALSE ) ) return;
break;
case FSM_STOPPING:
pCpCb->State = FSM_CLOSING;
if ( !FsmThisLayerFinished( pPcb, CpIndex, FALSE ) ) return;
break;
case FSM_REQ_SENT: case FSM_OPENED:
if ( !FsmThisLayerDown( pPcb, CpIndex ) ) return;
//
// Fallthru
//
case FSM_ACK_RCVD: case FSM_ACK_SENT:
InitRestartCounters( pPcb, pCpCb );
//
// May not be able to do this because the link may be down.
//
FsmSendTermReq( pPcb, CpIndex );
pCpCb->State = FSM_CLOSING;
break;
case FSM_CLOSING: case FSM_CLOSED: case FSM_INITIAL:
if ( !FsmThisLayerFinished( pPcb, CpIndex, FALSE ) ) return;
//
// nothing to do
//
break;
default:
PPP_ASSERT( pCpCb->State < 10 );
PppLog( 2,"Illegal transition->FsmClose received while in %s state", FsmStates[pCpCb->State] ); break;
} }
//**
//
// Call: FsmTimeout
//
// Returns: None
//
// Description: Called to process a timeout while waiting for reply
// from remote host.
//
VOID FsmTimeout( IN PCB * pPcb, IN DWORD CpIndex, IN DWORD Id, IN BOOL fAuthenticator ) { CPCB * pCpCb = GetPointerToCPCB( pPcb, CpIndex ); LCPCB * pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf);
if ( pCpCb == NULL ) { //
// If we got a timeout for an authentication CB that is no longer
// active, we just ignore it.
//
return; }
PppLog( 2, "Recv timeout event received for portid=%d,Id=%d,Protocol=%x,fAuth=%d", pPcb->dwPortId, Id, CpTable[CpIndex].CpInfo.Protocol, fAuthenticator );
//
// If we are authenticating we use the ConfigRetryCount
//
if ( CpIndex == GetCpIndexFromProtocol( fAuthenticator ? pLcpCb->Local.Work.AP : pLcpCb->Remote.Work.AP ) ) { if ( pPcb->PppPhase == PPP_AP ) { pCpCb = ( fAuthenticator ) ? &(pPcb->AuthenticatorCb) : &(pPcb->AuthenticateeCb);
//
// Silently discard timeouts for packets with Id < pPcb->LastId
//
if ( Id < pCpCb->LastId ) { return; }
if ( pCpCb->ConfigRetryCount > 0 ) { (pCpCb->ConfigRetryCount)--;
ApWork( pPcb, CpIndex, NULL, NULL, fAuthenticator ); } else { //
// If an error has already been set, do not change it.
//
if ( pPcb->LcpCb.dwError == NO_ERROR ) { pPcb->LcpCb.dwError = ERROR_PPP_TIMEOUT; }
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError ); } }
return; } else if ( CpIndex == GetCpIndexFromProtocol( PPP_CBCP_PROTOCOL ) ) { if ( pPcb->PppPhase == PPP_NEGOTIATING_CALLBACK ) { //
// Silently discard timeouts for packets with Id < pPcb->LastId
//
if ( Id < pCpCb->LastId ) { return; }
if ( pCpCb->ConfigRetryCount > 0 ) { (pCpCb->ConfigRetryCount)--;
CbWork( pPcb, CpIndex, NULL, NULL ); } else { //
// If an error has already been set, do not change it.
//
if ( pPcb->LcpCb.dwError == NO_ERROR ) { pPcb->LcpCb.dwError = ERROR_PPP_TIMEOUT; }
NotifyCallerOfFailure( pPcb, pPcb->LcpCb.dwError ); } } }
//
// Silently discard timeouts for packets with Id < pPcb->LastId
//
if ( Id < pCpCb->LastId ) { return; }
switch( pCpCb->State ) {
case FSM_REQ_SENT: case FSM_ACK_RCVD: case FSM_ACK_SENT:
if ( pCpCb->ConfigRetryCount > 0 ) { (pCpCb->ConfigRetryCount)--;
//
// If we have not received any PPP frames from the server yet.
//
if ( ( CpIndex == LCP_INDEX ) && ( pPcb->LcpCb.dwError == ERROR_PPP_NO_RESPONSE ) && ( !(pPcb->fFlags & PCBFLAG_IS_SERVER) ) ) { NotifyCaller( pPcb, PPPMSG_Progress, NULL ); }
// If the RestartTimer value is less then the configured
// restart timer value, then bump it up by one second.
//
if ( pPcb->RestartTimer < PppConfigInfo.DefRestartTimer ) { (pPcb->RestartTimer)++; }
if ( !FsmSendConfigReq( pPcb, CpIndex, TRUE ) ) return;
if ( pCpCb->State != FSM_ACK_SENT ) pCpCb->State = FSM_REQ_SENT; } else { PppLog( 1, "Request retry exceeded" );
//
// If the LCP layer exceeded its retry count
//
if ( pCpCb->dwError == NO_ERROR ) { pCpCb->dwError = ERROR_PPP_TIMEOUT; }
if ( !FsmThisLayerFinished( pPcb, CpIndex, TRUE ) ) return;
pCpCb->State = FSM_STOPPED; }
break;
case FSM_CLOSING: case FSM_STOPPING:
if ( pCpCb->TermRetryCount > 0 ) { (pCpCb->TermRetryCount)--;
FsmSendTermReq( pPcb, CpIndex ); } else { PppLog( 1, "Terminate retry exceeded" );
if ( pCpCb->dwError == NO_ERROR ) { pCpCb->dwError = ERROR_PPP_TIMEOUT; }
if ( !FsmThisLayerFinished( pPcb, CpIndex, TRUE ) ) return;
pCpCb->State = ( pCpCb->State == FSM_CLOSING ) ? FSM_CLOSED : FSM_STOPPED; }
break;
case FSM_OPENED: case FSM_INITIAL: case FSM_STARTING: case FSM_CLOSED: case FSM_STOPPED: default:
PPP_ASSERT( pCpCb->State < 10 );
PppLog( 2, "Illegal transition->FsmTimeout rcvd while in %s state", FsmStates[pCpCb->State] ); break; } }
//**
//
// Call: FsmReceive
//
// Returns: None
//
// Description: Called when a PPP packet is received. Will process the
// incomming packet.
//
VOID FsmReceive( IN PCB * pPcb, IN PPP_PACKET * pPacket, IN DWORD dwPacketLength ) { DWORD dwProtocol; DWORD CpIndex; PPP_CONFIG * pRecvConfig; CPCB * pCpCb; DWORD dwLength; LCPCB * pLcpCb = (LCPCB*)(pPcb->LcpCb.pWorkBuf); LogPPPPacket( TRUE, pPcb, pPacket, dwPacketLength ); //
// Validate length of packet
//
if ( dwPacketLength < ( PPP_PACKET_HDR_LEN + PPP_CONFIG_HDR_LEN ) ) { PppLog( 1, "Silently discarding badly formed packet" );
return; }
dwProtocol = WireToHostFormat16( pPacket->Protocol );
CpIndex = GetCpIndexFromProtocol( dwProtocol );
switch( pPcb->PppPhase ) { case PPP_NEGOTIATING_CALLBACK:
//
// Silently discard any packet other than LCP and Authentication
// and callback packets if we are in the callback phase
//
if ( CpIndex == GetCpIndexFromProtocol( PPP_CBCP_PROTOCOL ) ) break;
//
// Fallthru
//
case PPP_AP:
//
// Silently discard any packet other than LCP and Authentication
// packets if we are in the authentication phase
//
if ( CpIndex == GetCpIndexFromProtocol( pLcpCb->Local.Work.AP ) ) break;
if ( CpIndex == GetCpIndexFromProtocol( pLcpCb->Remote.Work.AP ) ) break;
//
// If we are the authenticatee being authenticated by EAP, then if we
// receive an NCP packet we should assume success. We do this by
// generating a fake EAP_SUCCESS message and send it to the
// authenticatee.
//
if ( ( pLcpCb->Remote.Work.AP == PPP_EAP_PROTOCOL ) && ( pPcb->PppPhase == PPP_AP ) ) { PPPAP_INPUT ApInput;
switch( dwProtocol ) { case PPP_CBCP_PROTOCOL: case PPP_IPCP_PROTOCOL: case PPP_ATCP_PROTOCOL: case PPP_IPXCP_PROTOCOL: case PPP_NBFCP_PROTOCOL: case PPP_CCP_PROTOCOL: case PPP_BACP_PROTOCOL:
PppLog( 1,"Received and NCP or CBCP packet before EAP success");
ZeroMemory( &ApInput, sizeof( ApInput ) );
ApInput.fSuccessPacketReceived = TRUE;
CpIndex = GetCpIndexFromProtocol( PPP_EAP_PROTOCOL );
if ( pPcb->AuthenticateeCb.LastId != (DWORD)-1 ) { RemoveFromTimerQ( pPcb->dwPortId, pPcb->AuthenticateeCb.LastId, pLcpCb->Remote.Work.AP, FALSE, TIMER_EVENT_TIMEOUT ); }
ApWork( pPcb, CpIndex, NULL, &ApInput, FALSE );
//
// PppPhase will now be one of PPP_LCP (if ApWork failed),
// PPP_NCP, or PPP_NEGOTIATING_CALLBACK. We will not call
// this function recursively again.
//
//
// Now process the NCP we received if we have passed
// authentication
//
if ( ( pPcb->PppPhase == PPP_NCP ) || ( pPcb->PppPhase == PPP_NEGOTIATING_CALLBACK ) ) { FsmReceive( pPcb, pPacket, dwPacketLength ); }
return;
default: break; } } //
// Fallthru
//
case PPP_LCP:
//
// Silently discard any packet other than LCP if we are in the
// LCP or termination or authentication phases
//
if ( CpIndex != LCP_INDEX ) { PppLog( 1, "Non-LCP packet received when LCP is not opened"); PppLog( 1, "Packet being silently discarded" );
return; }
break;
case PPP_NCP:
//
// We do not recognize this protocol
//
if ( CpIndex == (DWORD)-1 ) { if ( dwProtocol == PPP_BAP_PROTOCOL ) { CpIndex = GetCpIndexFromProtocol( PPP_BACP_PROTOCOL );
if (CpIndex != (DWORD)-1) { pCpCb = GetPointerToCPCB( pPcb, CpIndex );
if ( pCpCb->State == FSM_OPENED ) { BapEventReceive( pPcb->pBcb, pPacket, dwPacketLength ); return; } }
BapTrace( "BAP packet silently discarded" ); return; } //
// If this is a Control Protocol then we reject it, otherwise we
// we silently discard it.
// We used to also check if the protocol is less than 0x0000BFFF,
// but Shiva has proprietary NBFCP with id 0x0000CFEC and we need
// to protocol reject it.
//
if ( dwProtocol >= 0x00008000 ) { FsmSendProtocolRej( pPcb, pPacket, dwPacketLength ); } else { PppLog( 1, "Network-layer packet rcvd."); PppLog( 1, "Packet being silently discarded" ); }
return; }
break;
default:
PppLog( 1, "Packet received being silently discarded" ); return; }
pCpCb = GetPointerToCPCB( pPcb, CpIndex );
pRecvConfig = (PPP_CONFIG*)(pPacket->Information);
//
// We received a PPP packet so the remote host does support it
//
if ( pPcb->LcpCb.dwError == ERROR_PPP_NO_RESPONSE ) { pPcb->LcpCb.dwError = NO_ERROR; }
//
// If we received a packet for a protocol that we have but do not
// wish to configure then we send a configure reject.
//
if ( ( pCpCb == NULL ) || !(pCpCb->fConfigurable) ) { FsmSendProtocolRej( pPcb, pPacket, dwPacketLength );
return; }
dwLength = WireToHostFormat16( pRecvConfig->Length );
if ( ( dwLength > ( dwPacketLength - PPP_PACKET_HDR_LEN ) ) || ( dwLength < PPP_CONFIG_HDR_LEN ) ) { PppLog( 1,"Silently discarding badly formed packet" );
return; }
//
// Not in ProcessPacket table since parameters to this are different.
//
if ( ( CpIndex == LCP_INDEX ) && ( pRecvConfig->Code == PROT_REJ ) ) { ReceiveProtocolRej( pPcb, pPacket );
return; }
//
// Make sure that the protocol can handle the config code sent.
//
if ( ( pRecvConfig->Code == 0 ) || !( pRecvConfig->Code < CpTable[CpIndex].CpInfo.Recognize ) ) { ReceiveUnknownCode( pPcb, CpIndex, pCpCb, pRecvConfig );
return; }
//
// If we received an authentication packet.
//
if ( CpIndex == GetCpIndexFromProtocol( pLcpCb->Local.Work.AP ) ) { if ( ApIsAuthenticatorPacket( CpIndex, pRecvConfig->Code ) ) { if ( pPcb->AuthenticatorCb.LastId != (DWORD)-1 ) { //
// If we have just received a packet we have been waiting for
// then we stop the outstanding timout for it. We let the
// APs do the Id matching.
//
if ( pRecvConfig->Id == pPcb->AuthenticatorCb.LastId ) { RemoveFromTimerQ( pPcb->dwPortId, pPcb->AuthenticatorCb.LastId, pLcpCb->Local.Work.AP, TRUE, TIMER_EVENT_TIMEOUT ); } }
ApWork( pPcb, CpIndex, pRecvConfig, NULL, TRUE );
return; } else if ( CpIndex != GetCpIndexFromProtocol( pLcpCb->Remote.Work.AP ) ) { //
// Silently drop invalid packet, ie. Authenticatee's packet sent
// using Authenticater's protocol
//
PppLog( 1, "Authentication packet received being silently discarded");
return; } }
if ( CpIndex == GetCpIndexFromProtocol( pLcpCb->Remote.Work.AP ) ) { if ( !ApIsAuthenticatorPacket( CpIndex, pRecvConfig->Code ) ) { if ( pPcb->AuthenticateeCb.LastId != (DWORD)-1 ) { //
// If we have just received a packet we have been waiting for
// then we stop the outstanding timout for it. We let the
// APs do the Id matching.
//
if ( pRecvConfig->Id == pPcb->AuthenticateeCb.LastId ) { RemoveFromTimerQ( pPcb->dwPortId, pPcb->AuthenticateeCb.LastId, pLcpCb->Remote.Work.AP, FALSE, TIMER_EVENT_TIMEOUT ); } }
ApWork( pPcb, CpIndex, pRecvConfig, NULL, FALSE ); } else { //
// Silently drop invalid packet, ie. Authenticator's packet sent
// using Authenticatee's protocol
//
PppLog( 1, "Authentication packet received being silently discarded"); }
return; }
if ( CpIndex == GetCpIndexFromProtocol( PPP_CBCP_PROTOCOL ) ) { if ( pCpCb->LastId != (DWORD)-1 ) { //
// If we have just received a packet we have been waiting for
// then we stop the outstanding timout for it. We let the
// CBCP do the Id matching.
//
if ( pRecvConfig->Id == pCpCb->LastId ) { RemoveFromTimerQ( pPcb->dwPortId, pCpCb->LastId, PPP_CBCP_PROTOCOL, FALSE, TIMER_EVENT_TIMEOUT ); } } CbWork( pPcb, CpIndex, pRecvConfig, NULL );
} else { //
// Any combination of packets allowed here.
//
//if this is any packet and not Echo request packet,
//and if the flag is set then reset the flag.
if ( pRecvConfig->Code != ECHO_REPLY ) { if ( pPcb->fEchoRequestSend ) pPcb->fEchoRequestSend = 0; } (*ProcessPacket[pRecvConfig->Code])(pPcb, CpIndex, pCpCb, pRecvConfig); } }
|