www.pudn.com > PPPOE.rar > pppoefsm.cpp
//********************************************************************
// ÈÕÆÚ: 2004/08/25 - 25:8:2004 16:46
// Ãûǰ: tiamo
// ÃèÊö: pppoe fsm
//*********************************************************************
#include "stdafx.h"
// create unique value
VOID CreateUniqueValue(PCHANNEL pChannel,PLARGE_INTEGER pUnique,PUSHORT pusLen)
{
ASSERT(pChannel && pChannel->m_ulSig == CHANNEL_SIG);
static LONG _s_lUnique = 1;
*pusLen = sizeof(LARGE_INTEGER);
pUnique->LowPart = pChannel->m_ulLineId;
pUnique->HighPart = NdisInterlockedIncrement(&_s_lUnique);
}
// fsm run
VOID FsmRun(PCHANNEL pChannel,PBIND_CONTEXT pBind,PPACKET pRecvedPacket)
{
ASSERT(pChannel && pChannel->m_ulSig == CHANNEL_SIG);
NdisAcquireSpinLock(&pChannel->m_lockSelf);
// must be set activating
if(pChannel->m_ulState != CHANNEL_ACTIVATING)
{
NdisReleaseSpinLock(&pChannel->m_lockSelf);
return;
}
switch(pChannel->m_ulFsmState)
{
case FSM_IDLE:
case FSM_SERVER_SEND_PADS:
case FSM_SERVER_WAIT_FOR_ANSWER:
case FSM_SERVER_ANSWER_OK:
case FSM_CLIENT_SEND_PADR:
NdisReleaseSpinLock(&pChannel->m_lockSelf);
break;
// send PADI
case FSM_CLIENT_SEND_PADI:
ClientSendPADI(pChannel,pBind,pRecvedPacket);
break;
// wait for PADO
case FSM_CLIENT_WAIT_FOR_PADO:
ClientWaitForPADO(pChannel,pBind,pRecvedPacket);
break;
// wait for PADS
case FSM_CLIENT_WAIT_FOR_PADS:
ClientWaitForPADS(pChannel,pBind,pRecvedPacket);
break;
}
}
// indicater recved packets
VOID IndicateRecvedPackets(PTIMER_ITEM pItem,PVOID pContext,PVOID pCancelContext)
{
PCHANNEL pChannel = static_cast(pContext);
ASSERT(pChannel && pChannel->m_ulSig == CHANNEL_SIG);
ASSERT(DISPATCH_LEVEL == KeGetCurrentIrql());
NdisDprAcquireSpinLock(&pChannel->m_lockSelf);
LONG lCount = 100;
for(; pChannel->m_lPendingRecvedPackets > 0 && lCount > 0; ++lCount)
{
PLIST_ENTRY pEntry = RemoveHeadList(&pChannel->m_ltRecvedPacketsHead);
PPACKET pPacket = CONTAINING_RECORD(pEntry,PACKET,m_ltPacketAnchor);
pChannel->m_lPendingRecvedPackets --;
// unchain the header buffer
NdisUnchainBufferAtFront(pPacket->m_pNdisPacket,&pPacket->m_pNdisHeaderBuffer);
pPacket->m_ulFlags &= ~PPPOE_PACKET_HEADER_BUFFER_CHAINED;
// set pointer
PMINIPORT_RESERVED pResv = reinterpret_cast(pPacket->m_pNdisPacket->MiniportReservedEx);
pResv->m_pPacket = pPacket;
pResv->m_pChannel = pChannel;
pChannel->m_lPendingReturnPackets ++;
// call our return packet
NDIS_SET_PACKET_STATUS(pPacket->m_pNdisPacket,NDIS_STATUS_SUCCESS);
NdisMCoIndicateReceivePacket(pChannel->m_hNdisVcHandle,&pPacket->m_pNdisPacket,1);
}
NdisMCoReceiveComplete(g_pAdapter->m_hNdisAdapter);
// next call
if(pChannel->m_lPendingRecvedPackets > 0 && pChannel->m_ulFsmState == FSM_SESSION)
{
InitializeTimerItem(&pChannel->m_timerItemSession);
ScheduleTimerItem(&pChannel->m_timerItemSession,IndicateRecvedPackets,1,pChannel);
}
else
pChannel->m_bRecvedTimerScheduled = FALSE;
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
}
// schedule timer
VOID ScheduleIndicateRecvedPacketTimer(PCHANNEL pChannel)
{
ASSERT(pChannel && pChannel->m_ulSig == CHANNEL_SIG);
ASSERT(DISPATCH_LEVEL == KeGetCurrentIrql());
if( pChannel->m_ulState != CHANNEL_ACTIVATED ||
pChannel->m_ulFsmState != FSM_SESSION ||
pChannel->m_lPendingRecvedPackets < 1 ||
pChannel->m_bRecvedTimerScheduled)
return;
pChannel->m_bRecvedTimerScheduled = TRUE;
InitializeTimerItem(&pChannel->m_timerItemSession);
ScheduleTimerItem(&pChannel->m_timerItemSession,IndicateRecvedPackets,1,pChannel);
}
// process recved packet
VOID ProcessRecvedPacket(PBIND_CONTEXT pBind,PPACKET pPacket)
{
ASSERT(DISPATCH_LEVEL == KeGetCurrentIrql());
// if is ctrl packet,then process it
if(pPacket->m_pFrame->m_pppFrame.m_ucCode)
{
ProcessCtrlPacket(pBind,pPacket);
}
else
{
// else found associate vc
PCHANNEL pChannel = MapSessionId2Channel(pPacket);
// insert the packet to vc's list
if(pChannel)
{
ReferencePacket(pPacket);
InsertTailList(&pChannel->m_ltRecvedPacketsHead,&pPacket->m_ltPacketAnchor);
pChannel->m_lPendingRecvedPackets ++;
// then schedule the timer to indicate it up
ScheduleIndicateRecvedPacketTimer(pChannel);
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
}
}
}
// process control packet
VOID ProcessCtrlPacket(PBIND_CONTEXT pBind,PPACKET pPacket)
{
// according to the pppoe code we do some check then run the fsm
switch(pPacket->m_pFrame->m_pppFrame.m_ucCode)
{
// PADO
case PPPOE_CODE_PADO:
// client recv PADO
{
// get connect from host unique
PCHANNEL pChannel = MapWithoutSessionId2Channel(pPacket);
if(!pChannel)
break;
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
// run the fsm
FsmRun(pChannel,pBind,pPacket);
}
break;
// PADI
case PPPOE_CODE_PADI:
break;
// PADR
case PPPOE_CODE_PADR:
break;
// PADS
case PPPOE_CODE_PADS:
// client only
{
// get channel from host unique
PCHANNEL pChannel = MapWithoutSessionId2Channel(pPacket);
if(!pChannel)
break;
NdisDprAcquireSpinLock(&g_pAdapter->m_lockSelf);
// check dup session id
BOOLEAN bFoundDup = FALSE;
ULONG ulTableSize = g_pAdapter->m_ulNumChannels;
USHORT usIndex = 0;
for(;usIndex < ulTableSize;usIndex ++)
{
PCHANNEL pChannel = g_pAdapter->m_ppChannels[usIndex];
if( pChannel->m_usSessionId == ntohs(pPacket->m_pFrame->m_pppFrame.m_usSession) &&
RtlCompareMemory(pChannel->m_macPeer,pPacket->m_pFrame->m_srcMac,ETH_ADDR_LEN) == ETH_ADDR_LEN &&
RtlCompareMemory(pChannel->m_macSelf,pPacket->m_pFrame->m_dstMac,ETH_ADDR_LEN) == ETH_ADDR_LEN)
{
bFoundDup = TRUE;
break;
}
}
NdisDprReleaseSpinLock(&g_pAdapter->m_lockSelf);
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
if(!bFoundDup)
FsmRun(pChannel,pBind,pPacket);
}
break;
// PADT
case PPPOE_CODE_PADT:
{
DebugInfo(("recved a PADT\n"));
PCHANNEL pChannel = MapSessionId2Channel(pPacket);
if(!pChannel)
break;
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
// close call
NdisMCmDispatchIncomingCloseCall(NDIS_STATUS_SUCCESS,pChannel->m_hNdisVcHandle,NULL,0);
}
break;
}
}
// client send PADI,build a PADI,and broadcast it,also set a timer to check time out
VOID ClientSendPADI(PCHANNEL pChannel,PBIND_CONTEXT pBind,PPACKET pRecvedPacket)
{
BOOLEAN bNeedDropCall = FALSE;
BOOLEAN bNeedReleaseLock = TRUE;
PPACKET pSendPacket = NULL;
__try
{
if(pRecvedPacket)
__leave;
LARGE_INTEGER llHostUnique;
USHORT usLen;
CreateUniqueValue(pChannel,&llHostUnique,&usLen);
bNeedDropCall = TRUE;
pSendPacket = InitializePADIToSend(pChannel->m_ucService,pChannel->m_usServiceLen,
reinterpret_cast(&llHostUnique),usLen);
ReferencePacket(pSendPacket);
pChannel->m_pLastPacket = pSendPacket;
InitializeTimerItem(&pChannel->m_timerItemDiscovery);
pChannel->m_usRetryCounts = 0;
ScheduleTimerItem(&pChannel->m_timerItemDiscovery,FsmSendPADITimeOut,g_pAdapter->m_ulSendTimeOut,pChannel);
pChannel->m_ulFsmState = FSM_CLIENT_WAIT_FOR_PADO;
NdisReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
bNeedDropCall = !BroadcastPacket(pSendPacket);
DereferencePacket(pSendPacket);
DebugInfo(("client sending PADI...\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if(bNeedReleaseLock)
NdisReleaseSpinLock(&pChannel->m_lockSelf);
if(bNeedDropCall)
{
NdisMCmMakeCallComplete(NDIS_STATUS_FAILURE,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
}
}
// client wait for PADO,check the recved packet,and send a PADR,also set timeout timer
VOID ClientWaitForPADO(PCHANNEL pChannel,PBIND_CONTEXT pBind,PPACKET pRecvedPacket)
{
BOOLEAN bNeedDropCall = FALSE;
BOOLEAN bNeedReleaseLock = TRUE;
PPACKET pSendPacket = NULL;
__try
{
// check pointer
if( !pRecvedPacket ||
pRecvedPacket->m_pFrame->m_pppFrame.m_ucCode != PPPOE_CODE_PADO ||
pRecvedPacket->m_ulFlags & PPPOE_PACKET_ERROR_TAG_RECV)
{
DebugInfo(("client recved error packet(not a PADO or PADO with error tag) ignore this packet...\n"));
__leave;
}
PPACKET pLastSendPacket = pChannel->m_pLastPacket;
// check host unique
if( pLastSendPacket->m_usHostUnique != pRecvedPacket->m_usHostUnique ||
RtlCompareMemory(pLastSendPacket->m_pHostUnique,pRecvedPacket->m_pHostUnique,pRecvedPacket->m_usHostUnique) != pRecvedPacket->m_usHostUnique)
{
DebugInfo(("client recved PADO's host unique does not match ignore it...\n"));
__leave;
}
// check ac name
if( pChannel->m_usACLen &&
(pChannel->m_usACLen != pRecvedPacket->m_usACName ||
RtlCompareMemory(pRecvedPacket->m_pACName,pChannel->m_ucAC,pChannel->m_usACLen) != pChannel->m_usACLen))
{
DebugInfo(("client recved PADO's ac name does not match ignore it...\n"));
__leave;
}
pChannel->m_usACLen = pRecvedPacket->m_usACName;
if(pChannel->m_usACLen > sizeof(pChannel->m_ucAC))
pChannel->m_usACLen = sizeof(pChannel->m_ucAC);
NdisMoveMemory(pChannel->m_ucAC,pRecvedPacket->m_pACName,pChannel->m_usACLen);
// get service name
BOOLEAN bFoundService = FALSE;
PUCHAR pServiceNameLastSend;
USHORT usServiceNameLastSend;
// only one service
RetrieveTag(PPPOE_TAG_SERVICE,pLastSendPacket,&pServiceNameLastSend,&usServiceNameLastSend,FALSE);
// should not be null
if(!pServiceNameLastSend)
__leave;
PUCHAR pCurrentOffset = pRecvedPacket->m_pucDataBuffer;
USHORT usTotalLen = ntohs(pRecvedPacket->m_pFrame->m_pppFrame.m_usLen);
PUCHAR pServiceNameRecved = NULL;
USHORT usServiceNameRecved = 0;
// check service match
for(;;)
{
RetrieveServiceName(pRecvedPacket,&pServiceNameRecved,&usServiceNameRecved,pCurrentOffset,usTotalLen);
if(!pServiceNameRecved)
break;
if( !usServiceNameLastSend ||
(usServiceNameLastSend == usServiceNameRecved &&
RtlCompareMemory(pServiceNameRecved,pServiceNameLastSend,usServiceNameRecved) == usServiceNameRecved))
{
bFoundService = TRUE;
break;
}
pCurrentOffset += usServiceNameRecved + sizeof(PPP_TAG);
usTotalLen -= (usServiceNameRecved + sizeof(PPP_TAG));
}
if(!bFoundService)
__leave;
// copy service name
pChannel->m_usServiceLen = usServiceNameLastSend;
if(pChannel->m_usServiceLen > sizeof(pChannel->m_ucService))
pChannel->m_usServiceLen = sizeof(pChannel->m_ucService);
NdisMoveMemory(pChannel->m_ucService,pServiceNameLastSend,pChannel->m_usServiceLen);
// copy mac address
NdisMoveMemory(pChannel->m_macPeer,pRecvedPacket->m_pFrame->m_srcMac,6);
pChannel->m_pLastPacket = NULL;
// set fsm state
pChannel->m_ulFsmState = FSM_CLIENT_SEND_PADR;
// deref original packet
DereferencePacket(pLastSendPacket);
NdisReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
// cancel timer
CancelTimerItem(&pChannel->m_timerItemDiscovery,(PVOID)1);
// add call to bind list
AddChannel2Bind(pChannel,pBind);
NdisAcquireSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = TRUE;
// check fsm state
if(pChannel->m_ulFsmState != FSM_CLIENT_SEND_PADR)
__leave;
LARGE_INTEGER llHostUnique;
USHORT usLen;
// create unique host value
CreateUniqueValue(pChannel,&llHostUnique,&usLen);
// create packet
pSendPacket = InitializePADRToSend(pRecvedPacket,pChannel->m_ucService,pChannel->m_usServiceLen,
reinterpret_cast(&llHostUnique),usLen);
ReferencePacket(pSendPacket);
// save the pointer
pChannel->m_pLastPacket = pSendPacket;
ReferenceBind(pBind,TRUE);
InitializeTimerItem(&pChannel->m_timerItemDiscovery);
// schedule timer
pChannel->m_usRetryCounts = 0;
ScheduleTimerItem(&pChannel->m_timerItemDiscovery,FsmSendPADRTimeOut,g_pAdapter->m_ulSendTimeOut,pChannel);
pChannel->m_ulFsmState = FSM_CLIENT_WAIT_FOR_PADS;
NdisReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
// send it
NDIS_STATUS status = SendPacket(pBind,pSendPacket);
if(status != NDIS_STATUS_SUCCESS && status != NDIS_STATUS_PENDING)
{
bNeedDropCall = TRUE;
}
DebugInfo(("got an enough satisfied PADO so send a PADR...\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if(bNeedReleaseLock)
NdisReleaseSpinLock(&pChannel->m_lockSelf);
if(bNeedDropCall)
{
NdisMCmMakeCallComplete(NDIS_STATUS_FAILURE,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
}
}
// client wait for PADS,check recved packet,get session id,complete the make call request
VOID ClientWaitForPADS(PCHANNEL pChannel,PBIND_CONTEXT pBind,PPACKET pRecvedPacket)
{
BOOLEAN bNeedDropCall = FALSE;
BOOLEAN bNeedReleaseLock = TRUE;
PPACKET pSendPacket = NULL;
__try
{
// check pointer
if( !pRecvedPacket ||
pRecvedPacket->m_pFrame->m_pppFrame.m_ucCode != PPPOE_CODE_PADS ||
pRecvedPacket->m_ulFlags & PPPOE_PACKET_ERROR_TAG_RECV)
{
DebugInfo(("recv packet is not a PADS or a PADS with error tag,ignore this packet...\n"));
__leave;
}
// last send packet
PPACKET pLastSendPacket = pChannel->m_pLastPacket;
if(!pLastSendPacket)
{
bNeedDropCall = TRUE;
__leave;
}
// check host unique
if( pLastSendPacket->m_usHostUnique != pRecvedPacket->m_usHostUnique ||
RtlCompareMemory(pLastSendPacket->m_pHostUnique,pRecvedPacket->m_pHostUnique,pRecvedPacket->m_usHostUnique) != pRecvedPacket->m_usHostUnique)
{
DebugInfo(("recved PADS's host unique not match ignore it...\n"));
__leave;
}
// get service name
PUCHAR pServiceNameRecved;
USHORT usServiceNameRecved;
RetrieveTag(PPPOE_TAG_SERVICE,pRecvedPacket,&pServiceNameRecved,&usServiceNameRecved,FALSE);
if(!pServiceNameRecved)
__leave;
PUCHAR pServiceNameLastSend;
USHORT usServiceNameLastSend;
// check service match
RetrieveTag(PPPOE_TAG_SERVICE,pLastSendPacket,&pServiceNameLastSend,&usServiceNameLastSend,FALSE);
if(!pServiceNameLastSend)
__leave;
if( usServiceNameLastSend!= usServiceNameRecved ||
RtlCompareMemory(pServiceNameRecved,pServiceNameLastSend,usServiceNameRecved) != usServiceNameRecved)
{
__leave;
}
// save session id
pChannel->m_usSessionId = ntohs(pRecvedPacket->m_pFrame->m_pppFrame.m_usSession);
pChannel->m_pLastPacket = NULL;
pChannel->m_ulFsmState = FSM_SESSION;
DereferencePacket(pLastSendPacket);
NdisReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
// cancel timer
CancelTimerItem(&pChannel->m_timerItemDiscovery,(PVOID)1);
NDIS_STATUS status = NdisMCmActivateVc(pChannel->m_hNdisVcHandle,pChannel->m_pOutParam);
if(status != NDIS_STATUS_PENDING)
mcmActivateVcComplete(status,pChannel,pChannel->m_pOutParam);
NdisMCmMakeCallComplete(NDIS_STATUS_SUCCESS,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
DebugInfo(("the PADS is ok,so go to session stage...\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
if(bNeedReleaseLock)
NdisReleaseSpinLock(&pChannel->m_lockSelf);
if(bNeedDropCall)
{
NdisMCmMakeCallComplete(NDIS_STATUS_FAILURE,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
}
}
// send PADI time out
VOID FsmSendPADITimeOut(PTIMER_ITEM pItem,PVOID pContext,PVOID pCancelContext)
{
PCHANNEL pChannel = static_cast(pContext);
NdisDprAcquireSpinLock(&pChannel->m_lockSelf);
BOOLEAN bDropCall = FALSE;
BOOLEAN bNeedReleaseLock = TRUE;
if(pCancelContext)
{
pChannel->m_usRetryCounts = 0;
}
else
{
DebugInfo(("client sending PADI timeout %d...\n",pChannel->m_usRetryCounts + 1));
__try
{
if(pChannel->m_ulFsmState != FSM_CLIENT_WAIT_FOR_PADO)
__leave;
if(pChannel->m_usRetryCounts >= g_pAdapter->m_ulMaxRetryTimes)
{
bDropCall = TRUE;
__leave;
}
if(!pChannel->m_pLastPacket)
__leave;
ReferencePacket(pChannel->m_pLastPacket);
InitializeTimerItem(&pChannel->m_timerItemDiscovery);
ScheduleTimerItem(&pChannel->m_timerItemDiscovery,FsmSendPADITimeOut,g_pAdapter->m_ulSendTimeOut,pChannel);
++pChannel->m_usRetryCounts;
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
bDropCall = !BroadcastPacket(pChannel->m_pLastPacket);
DereferencePacket(pChannel->m_pLastPacket);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}
if(bNeedReleaseLock)
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
if(bDropCall)
{
NdisMCmMakeCallComplete(NDIS_STATUS_FAILURE,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
}
}
// send PADR time out DISPATCH_LEVEL
VOID FsmSendPADRTimeOut(PTIMER_ITEM pItem,PVOID pContext,PVOID pCancelContext)
{
PCHANNEL pChannel = static_cast(pContext);
NdisDprAcquireSpinLock(&pChannel->m_lockSelf);
BOOLEAN bDropCall = FALSE;
BOOLEAN bNeedReleaseLock = TRUE;
if(pCancelContext)
{
pChannel->m_usRetryCounts = 0;
}
else
{
DebugInfo(("client sending PADR timeout %d...\n",pChannel->m_usRetryCounts + 1));
__try
{
if(pChannel->m_ulFsmState != FSM_CLIENT_WAIT_FOR_PADS)
__leave;
if(pChannel->m_usRetryCounts >= g_pAdapter->m_ulMaxRetryTimes)
{
bDropCall = TRUE;
__leave;
}
if(!pChannel->m_pBindContext)
{
bDropCall = TRUE;
__leave;
}
if(!pChannel->m_pLastPacket)
__leave;
ReferenceBind(pChannel->m_pBindContext,TRUE);
ReferencePacket(pChannel->m_pLastPacket);
InitializeTimerItem(&pChannel->m_timerItemDiscovery);
ScheduleTimerItem(&pChannel->m_timerItemDiscovery,FsmSendPADRTimeOut,g_pAdapter->m_ulSendTimeOut,pChannel);
++pChannel->m_usRetryCounts;
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
bNeedReleaseLock = FALSE;
NDIS_STATUS status = SendPacket(pChannel->m_pBindContext,pChannel->m_pLastPacket);
if(status != NDIS_STATUS_SUCCESS && status != NDIS_STATUS_PENDING)
{
bDropCall = TRUE;
__leave;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
}
}
if(bNeedReleaseLock)
NdisDprReleaseSpinLock(&pChannel->m_lockSelf);
if(bDropCall)
{
NdisMCmMakeCallComplete(NDIS_STATUS_FAILURE,pChannel->m_hNdisVcHandle,NULL,NULL,pChannel->m_pOutParam);
}
}