xbox-kernel/private/ntos/xnet/ppp/receive.cpp
2020-09-30 17:17:25 +02:00

621 lines
15 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
receive.cpp
Abstract:
Handle received packets.
Revision History:
07-18-00 vadimg created
--*/
#include "precomp.h"
VOID (*gReceive[])(DWORD CpIndex, PPP_CONFIG *pRecvConfig) = {
NULL,
ReceiveConfigReq,
ReceiveConfigAck,
ReceiveConfigNakRej,
ReceiveConfigNakRej,
ReceiveTermReq,
ReceiveTermAck,
ReceiveCodeRej,
NULL,
ReceiveEchoReq,
ReceiveEchoReply,
ReceiveDiscardReq
};
/***************************************************************************\
* ReceiveConfigReq
*
\***************************************************************************/
VOID ReceiveConfigReq(DWORD CpIndex, PPP_CONFIG *pRecvConfig)
{
PPP_PACKET *pSendPacket = GetSendPacket();
PPP_CONFIG *pSendConfig = (PPP_CONFIG*)pSendPacket->Data;
BOOL fAcked;
switch (gInfo.State) {
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
if (!FsmSendConfigReq(CpIndex)) {
return;
}
if (!FsmSendConfigResult(CpIndex, pRecvConfig, &fAcked)) {
return;
}
gInfo.State = fAcked ? FSM_ACK_SENT : FSM_REQ_SENT;
break;
case FSM_STOPPED:
InitRestartCounters();
if (!FsmSendConfigReq(CpIndex)) {
return;
}
// fall through
case FSM_REQ_SENT:
case FSM_ACK_SENT:
if (!FsmSendConfigResult(CpIndex, pRecvConfig, &fAcked)) {
return;
}
gInfo.State = fAcked ? FSM_ACK_SENT : FSM_REQ_SENT;
break;
case FSM_ACK_RCVD:
if (!FsmSendConfigResult(CpIndex, pRecvConfig, &fAcked)) {
return;
}
if (fAcked) {
gInfo.State = FSM_OPENED;
FsmThisLayerUp(CpIndex);
}
break;
case FSM_CLOSED:
FsmSendTermAck(CpIndex, pRecvConfig);
break;
case FSM_CLOSING:
case FSM_STOPPING:
break;
}
}
/***************************************************************************\
* ReceiveConfigAck
*
\***************************************************************************/
VOID ReceiveConfigAck(DWORD CpIndex, PPP_CONFIG *pRecvConfig)
{
// The Id of the Ack HAS to match the Id of the last request sent
// If it is different, then we should silently discard it.
if (pRecvConfig->Id != gInfo.LastId) {
return;
}
ClearTimeout();
switch (gInfo.State) {
case FSM_REQ_SENT:
if (!FsmConfigResultReceived(CpIndex, pRecvConfig)) {
return;
}
InitRestartCounters();
gInfo.State = FSM_ACK_RCVD;
break;
case FSM_ACK_SENT:
if (!FsmConfigResultReceived(CpIndex, pRecvConfig)) {
return;
}
InitRestartCounters();
gInfo.State = FSM_OPENED;
FsmThisLayerUp(CpIndex);
break;
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
// fall through
case FSM_ACK_RCVD:
if (!FsmSendConfigReq(CpIndex)) {
return;
}
gInfo.State = FSM_REQ_SENT;
break;
case FSM_CLOSED:
case FSM_STOPPED:
// Out of Sync; kill the remote
FsmSendTermAck(CpIndex, pRecvConfig);
break;
case FSM_CLOSING:
case FSM_STOPPING:
break;
}
}
/***************************************************************************\
* ReceiveConfigNakRej
*
\***************************************************************************/
VOID ReceiveConfigNakRej(DWORD CpIndex, PPP_CONFIG *pRecvConfig)
{
// The Id of the Nak/Rej HAS to match the Id of the last request sent
// If it is different, then we should silently discard it.
if (pRecvConfig->Id != gInfo.LastId) {
return;
}
ClearTimeout();
switch (gInfo.State) {
case FSM_REQ_SENT:
case FSM_ACK_SENT:
if (!FsmConfigResultReceived(CpIndex, pRecvConfig)) {
return;
}
InitRestartCounters();
if (!FsmSendConfigReq(CpIndex)) {
return;
}
break;
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
// fall through
case FSM_ACK_RCVD:
if (!FsmSendConfigReq(CpIndex)) {
return;
}
gInfo.State = FSM_REQ_SENT;
break;
case FSM_CLOSED:
case FSM_STOPPED:
// Out of Sync; kill the remote
FsmSendTermAck(CpIndex, pRecvConfig);
break;
case FSM_CLOSING:
case FSM_STOPPING:
break;
}
}
/***************************************************************************\
* ReceiveTermReq
*
\***************************************************************************/
VOID ReceiveTermReq(DWORD CpIndex, PPP_CONFIG *pConfig)
{
switch (gInfo.State) {
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
FsmSendTermAck(CpIndex, pConfig);
gInfo.State = FSM_STOPPING;
break;
case FSM_ACK_RCVD:
case FSM_ACK_SENT:
case FSM_REQ_SENT:
FsmSendTermAck(CpIndex, pConfig);
gInfo.State = FSM_REQ_SENT;
break;
case FSM_CLOSED:
case FSM_CLOSING:
case FSM_STOPPED:
case FSM_STOPPING:
FsmSendTermAck(CpIndex, pConfig);
break;
}
FsmThisLayerFinished(CpIndex);
}
/***************************************************************************\
* ReceiveTermAck
*
\***************************************************************************/
VOID ReceiveTermAck(DWORD CpIndex, PPP_CONFIG *pRecvConfig)
{
// The Id of the Term Ack HAS to match the Id of the last request sent
// If it is different, then we should silently discard it.
if (pRecvConfig->Id != gInfo.LastId) {
return;
}
switch (gInfo.State) {
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
if (!FsmSendConfigReq(CpIndex)) {
return;
}
gInfo.State = FSM_REQ_SENT;
break;
case FSM_ACK_RCVD:
gInfo.State = FSM_REQ_SENT;
break;
case FSM_CLOSING:
case FSM_STOPPING:
if (!FsmThisLayerFinished(CpIndex)) {
return;
}
gInfo.State = (gInfo.State == FSM_CLOSING) ? FSM_CLOSED : FSM_STOPPED;
break;
case FSM_REQ_SENT:
case FSM_ACK_SENT:
case FSM_CLOSED:
case FSM_STOPPED:
break;
}
}
/***************************************************************************\
* ReceiveUnknownCode
*
\***************************************************************************/
VOID ReceiveUnknownCode(DWORD CpIndex, PPP_CONFIG *pConfig)
{
switch (gInfo.State) {
case FSM_STOPPED:
case FSM_STOPPING:
case FSM_OPENED:
case FSM_ACK_SENT:
case FSM_ACK_RCVD:
case FSM_REQ_SENT:
case FSM_CLOSING:
case FSM_CLOSED:
FsmSendCodeReject(CpIndex, pConfig);
break;
}
}
/***************************************************************************\
* ReceiveDiscardReq
*
\***************************************************************************/
VOID ReceiveDiscardReq(DWORD CpIndex, PPP_CONFIG *pConfig)
{
}
/***************************************************************************\
* ReceiveEchoReq
*
\***************************************************************************/
VOID ReceiveEchoReq(DWORD CpIndex, PPP_CONFIG *pConfig)
{
// Silently discard this packet if LCP is not in an opened state
if (!IsLcpOpened()) {
return;
}
switch (gInfo.State) {
case FSM_STOPPED:
case FSM_STOPPING:
case FSM_ACK_SENT:
case FSM_ACK_RCVD:
case FSM_REQ_SENT:
case FSM_CLOSING:
case FSM_CLOSED:
case FSM_STARTING:
case FSM_INITIAL:
break;
case FSM_OPENED:
FsmSendEchoReply(CpIndex, pConfig);
break;
}
}
/***************************************************************************\
* ReceiveEchoReply
*
\***************************************************************************/
VOID ReceiveEchoReply(DWORD CpIndex, PPP_CONFIG *pConfig)
{
}
/***************************************************************************\
* ReceiveCodeRej
*
\***************************************************************************/
VOID ReceiveCodeRej(DWORD CpIndex, PPP_CONFIG *pConfig)
{
pConfig = (PPP_CONFIG*)pConfig->Data;
// First check to see if these codes may be rejected without
// affecting implementation. Permitted code rejects
if (CpIndex == CP_LCP) {
switch (pConfig->Code) {
case CONFIG_REQ:
case CONFIG_ACK:
case CONFIG_NAK:
case CONFIG_REJ:
case TERM_REQ:
case TERM_ACK:
case CODE_REJ:
case PROT_REJ:
case ECHO_REQ:
case ECHO_REPLY:
case DISCARD_REQ:
// Unpermitted code rejects.
break;
default:
// Permitted code rejects, we can still work.
switch (gInfo.State) {
case FSM_ACK_RCVD:
gInfo.State = FSM_REQ_SENT;
break;
}
return;
}
}
// Actually the remote side did not reject the protocol, it rejected
// the code. But for all practical purposes we cannot talk with
// the corresponding CP on the remote side. This is actually an
// implementation error in the remote side.
gInfo.dwError = ERROR_PPP_NOT_CONVERGING;
switch (gInfo.State) {
case FSM_CLOSING:
if (!FsmThisLayerFinished(CpIndex)) {
return;
}
gInfo.State = FSM_CLOSED;
break;
case FSM_REQ_SENT:
case FSM_ACK_RCVD:
case FSM_ACK_SENT:
case FSM_STOPPING:
if (!FsmThisLayerFinished(CpIndex)) {
return;
}
gInfo.State = FSM_STOPPED;
break;
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
InitRestartCounters();
FsmSendTermReq(CpIndex);
gInfo.State = FSM_STOPPING;
break;
case FSM_CLOSED:
case FSM_STOPPED:
break;
}
}
/***************************************************************************\
* ReceiveProtocolRej
*
\***************************************************************************/
VOID ReceiveProtocolRej(PPP_PACKET *pPacket)
{
PPP_CONFIG *pRecvConfig = (PPP_CONFIG*)pPacket->Data;
DWORD dwProtocol = WireToHostFormat16(pRecvConfig->Data);
DWORD CpIndex;
CpIndex = GetCpIndexFromProtocol(dwProtocol);
if (CpIndex == (DWORD)-1) {
return;
}
if (CpIndex == CP_LCP) {
gInfo.dwError = ERROR_PPP_NOT_CONVERGING;
return;
}
// If LCP is not in the opened state we silently discard this packet
if (!IsLcpOpened()) {
return;
}
gInfo.dwError = ERROR_PPP_CP_REJECTED;
switch (gInfo.State) {
case FSM_CLOSING:
if (!FsmThisLayerFinished(CpIndex)) {
return;
}
gInfo.State = FSM_CLOSED;
break;
case FSM_REQ_SENT:
case FSM_ACK_RCVD:
case FSM_ACK_SENT:
case FSM_STOPPING:
if (!FsmThisLayerFinished(CpIndex)) {
return;
}
gInfo.State = FSM_STOPPED;
break;
case FSM_OPENED:
if (!FsmThisLayerDown(CpIndex)) {
return;
}
InitRestartCounters();
FsmSendTermReq(CpIndex);
gInfo.State = FSM_STOPPING;
break;
case FSM_CLOSED:
case FSM_STOPPED:
break;
}
}
/***************************************************************************\
* FsmConfigResultReceived
*
\***************************************************************************/
BOOL FsmConfigResultReceived(DWORD CpIndex, PPP_CONFIG *pRecvConfig)
{
DWORD dwRetCode;
switch (pRecvConfig->Code) {
case CONFIG_NAK:
dwRetCode = gCpTable[CpIndex].CP_ConfigNakReceived(pRecvConfig);
break;
case CONFIG_ACK:
dwRetCode = gCpTable[CpIndex].CP_ConfigAckReceived(pRecvConfig);
break;
case CONFIG_REJ:
dwRetCode = gCpTable[CpIndex].CP_ConfigRejReceived(pRecvConfig);
break;
default:
return FALSE;
}
if (dwRetCode != NO_ERROR) {
if (dwRetCode == ERROR_PPP_INVALID_PACKET) {
return TRUE;
} else {
FsmClose(CpIndex);
return FALSE;
}
}
return TRUE;
}
/***************************************************************************\
* FsmReceive
*
\***************************************************************************/
VOID FsmReceive(PPP_PACKET *pPacket, DWORD dwPacketLength)
{
DWORD dwProtocol;
DWORD CpIndex;
PPP_CONFIG *pRecvConfig;
DWORD dwLength;
BOOL fAuth = FALSE;
// Validate length of packet
if (dwPacketLength < (PPP_PACKET_HDR_LEN + PPP_CONFIG_HDR_LEN)) {
return;
}
dwProtocol = WireToHostFormat16(pPacket->Protocol);
CpIndex = GetCpIndexFromProtocol(dwProtocol);
if (CpIndex == (DWORD)-1) {
DbgPrint("Unknown protocol in PPP packet\n");
return;
}
switch (gInfo.Phase) {
case PPP_AP:
if (CpIndex == GetCpIndexFromProtocol(gLcp.Remote.Work.AP)) {
fAuth = TRUE;
break;
}
// fall through
case PPP_LCP:
if (CpIndex == CP_LCP) {
break;
}
return;
}
pRecvConfig = (PPP_CONFIG*)pPacket->Data;
dwLength = WireToHostFormat16(pRecvConfig->Length);
if (dwLength > (dwPacketLength - PPP_PACKET_HDR_LEN) ||
(dwLength < PPP_CONFIG_HDR_LEN)) {
return;
}
// Not in ProcessPacket table since parameters to this are different.
if (CpIndex == CP_LCP && pRecvConfig->Code == PROT_REJ) {
ReceiveProtocolRej(pPacket);
return;
}
// Make sure that the protocol can handle the config code sent.
if (pRecvConfig->Code == 0 ||
pRecvConfig->Code > gCpTable[CpIndex].Recognize) {
ReceiveUnknownCode(CpIndex, pRecvConfig);
return;
}
if (fAuth) {
ApWork(CpIndex, pRecvConfig);
} else {
gReceive[pRecvConfig->Code](CpIndex, pRecvConfig);
}
}