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

714 lines
22 KiB
C++

/*++
Copyright (c) 2000 Microsoft Corporation
lcp.cpp
Abstract:
Line Control Protocol.
Revision History:
07-11-00 vadimg created
--*/
#include "precomp.h"
LCP_OPTIONS gLcpDefault = {
0, // Negotiation flags
LCP_DEFAULT_MRU, // Default value for MRU
0xFFFFFFFF, // Default ACCM value.
0, // no authentication ( for client )
0, // no authentication data ( for client )
NULL, // no authentication data ( for client )
0, // Magic Number.
FALSE, // Protocol field compression.
FALSE, // Address and Contorl-Field Compression.
0, // Callback Operation message field
LCP_DEFAULT_MRU, // Default value for MRRU == MRU according to RFC1717
0, // No short sequencing
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // No endpoint discriminator
0, // Length of Endpoint Discriminator
0 // Link Discriminator (for BAP/BACP)
};
DWORD gSizeOfOption[] = {
0, // unused
PPP_OPTION_HDR_LEN + 2, // MRU
PPP_OPTION_HDR_LEN + 4, // ACCM
PPP_OPTION_HDR_LEN + 2, // authentication
0, // Unused.
PPP_OPTION_HDR_LEN + 4, // magic number
0, // Reserved, unused
PPP_OPTION_HDR_LEN + 0, // Protocol compression
PPP_OPTION_HDR_LEN + 0, // Address/Control compression
0, // Unused
0, // Unused
0, // Unused
0, // Unused
PPP_OPTION_HDR_LEN + 1, // Callback
0, // Unused
0, // Unused
0, // Unused
PPP_OPTION_HDR_LEN + 2, // MRRU
PPP_OPTION_HDR_LEN + 0, // Short Sequence Header Format
PPP_OPTION_HDR_LEN, // Endpoint Discriminator
0, // Unused
0, // Unused
0, // Unused
PPP_OPTION_HDR_LEN + 2 // Link Discriminator (for BAP/BACP)
};
DWORD gLcpNegotiate = LCP_N_MRU | LCP_N_ACCM | LCP_N_AUTHENT | LCP_N_MAGIC | LCP_N_PFC | LCP_N_ACFC;
/***************************************************************************\
* MakeOption
*
\***************************************************************************/
DWORD MakeOption(LCP_OPTIONS *pOption, DWORD dwOptionCode,
PPP_OPTION *pSendOption, DWORD cbSendOption)
{
if (cbSendOption < gSizeOfOption[dwOptionCode]) {
return ERROR_BUFFER_TOO_SMALL;
}
pSendOption->Type = (BYTE)dwOptionCode;
pSendOption->Length = (BYTE)gSizeOfOption[dwOptionCode];
switch (dwOptionCode) {
case LCP_OPTION_MRU:
HostToWireFormat16((WORD)pOption->MRU, pSendOption->Data);
break;
case LCP_OPTION_ACCM:
HostToWireFormat32(pOption->ACCM, pSendOption->Data);
break;
case LCP_OPTION_AUTHENT:
if (cbSendOption < (gSizeOfOption[dwOptionCode] + pOption->APDataSize)) {
return ERROR_BUFFER_TOO_SMALL;
}
HostToWireFormat16((WORD)pOption->AP, pSendOption->Data);
CopyMemory(pSendOption->Data + 2, pOption->pAPData, pOption->APDataSize);
pSendOption->Length += (BYTE)pOption->APDataSize;
break;
case LCP_OPTION_MAGIC:
HostToWireFormat32(pOption->MagicNumber, pSendOption->Data);
break;
case LCP_OPTION_PFC:
break;
case LCP_OPTION_ACFC:
break;
case LCP_OPTION_MRRU:
HostToWireFormat16((WORD)pOption->MRRU, pSendOption->Data);
break;
default:
return ERROR_INVALID_PARAMETER;
}
return NO_ERROR;
}
/***************************************************************************\
* CheckOption
*
\***************************************************************************/
DWORD CheckOption(LCP_SIDE *pLcpSide, PPP_OPTION *pOption, BOOL fMakingResult)
{
DWORD dwIndex;
DWORD dwAPDataSize;
DWORD dwRetCode = CONFIG_ACK;
if (pOption->Length < gSizeOfOption[pOption->Type]) {
return CONFIG_REJ;
}
// If we do not want to negotiate the option we CONFIG_REJ it.
if (!(pLcpSide->WillNegotiate & (1 << pOption->Type))) {
return CONFIG_REJ;
}
switch (pOption->Type) {
case LCP_OPTION_MRU:
pLcpSide->Work.MRU = WireToHostFormat16(pOption->Data);
// Check to see if this value is appropriate
if (fMakingResult) {
// We cannot send packets smaller than LCP_DEFAULT_MRU
if (pLcpSide->Work.MRU < LCP_DEFAULT_MRU) {
pLcpSide->Work.MRU = pLcpSide->Want.MRU;
dwRetCode = CONFIG_NAK;
}
} else {
// We cannot receive bigger packets.
if (pLcpSide->Work.MRU > pLcpSide->Want.MRU) {
pLcpSide->Work.MRU = pLcpSide->Want.MRU;
dwRetCode = CONFIG_NAK;
}
}
break;
case LCP_OPTION_ACCM:
pLcpSide->Work.ACCM = WireToHostFormat32(pOption->Data);
// If we are responding to a request, we accept it blindly, if we are
// processing a NAK, then the remote host may ask to escape more
// control characters than we require, but must escape at least the
// control chars that we require.
if (!fMakingResult) {
if (pLcpSide->Work.ACCM !=
(pLcpSide->Work.ACCM | pLcpSide->Want.ACCM)) {
pLcpSide->Work.ACCM |= pLcpSide->Want.ACCM;
dwRetCode = CONFIG_NAK;
}
}
break;
case LCP_OPTION_AUTHENT:
pLcpSide->Work.AP = WireToHostFormat16( pOption->Data );
// If there was Authentication data.
if (pOption->Length > PPP_OPTION_HDR_LEN + 2) {
dwAPDataSize = pOption->Length - PPP_OPTION_HDR_LEN - 2;
if (dwAPDataSize != pLcpSide->Work.APDataSize) {
pLcpSide->Work.APDataSize = dwAPDataSize;
if (pLcpSide->Work.pAPData != NULL) {
Free(pLcpSide->Work.pAPData);
pLcpSide->Work.pAPData = NULL;
}
pLcpSide->Work.pAPData = (PBYTE)Alloc(pLcpSide->Work.APDataSize);
if (pLcpSide->Work.pAPData == NULL) {
pLcpSide->Work.APDataSize = 0;
return CONFIG_REJ;
}
}
CopyMemory(pLcpSide->Work.pAPData, pOption->Data + 2,
pLcpSide->Work.APDataSize);
} else {
pLcpSide->Work.APDataSize = 0;
}
switch (pLcpSide->Work.AP) {
case PPP_PAP_PROTOCOL:
if (!(pLcpSide->APsAvailable & LCP_AP_PAP)) {
dwRetCode = CONFIG_NAK;
}
break;
default:
dwRetCode = CONFIG_NAK;
break;
}
break;
case LCP_OPTION_MAGIC:
pLcpSide->Work.MagicNumber = WireToHostFormat32(pOption->Data);
if (fMakingResult) {
// Ensure that magic numbers are different and that the remote
// request does not contain a magic number of 0.
if ((pLcpSide->Work.MagicNumber == gLcp.Local.Work.MagicNumber) ||
(pLcpSide->Work.MagicNumber == 0)) {
if (pLcpSide->Work.MagicNumber == gLcp.Local.Work.MagicNumber) {
++gLcp.dwMagicNumberFailureCount;
}
pLcpSide->Work.MagicNumber = GetMagicNumber();
dwRetCode = CONFIG_NAK;
}
} else {
// The remote peer NAK'ed with a magic number, check to see if
// the magic number in the NAK is the same as what we NAK'ed last
if (pLcpSide->Work.MagicNumber == gLcp.Remote.Work.MagicNumber) {
++gLcp.dwMagicNumberFailureCount;
pLcpSide->Work.MagicNumber = GetMagicNumber();
dwRetCode = CONFIG_NAK;
}
}
break;
case LCP_OPTION_PFC:
pLcpSide->Work.PFC = TRUE;
if (pLcpSide->Want.PFC == FALSE) {
dwRetCode = CONFIG_REJ;
}
break;
case LCP_OPTION_ACFC:
pLcpSide->Work.ACFC = TRUE;
if (pLcpSide->Want.ACFC == FALSE) {
dwRetCode = CONFIG_REJ;
}
break;
case LCP_OPTION_MRRU:
pLcpSide->Work.MRRU = WireToHostFormat16(pOption->Data);
// Check to see if this value is appropriate
if (fMakingResult) {
// We cannot send smaller reconstructed packets.
if (pLcpSide->Work.MRRU < pLcpSide->Want.MRRU) {
pLcpSide->Work.MRRU = pLcpSide->Want.MRRU;
dwRetCode = CONFIG_NAK;
}
} else {
// We cannot receive bigger reconstructed packets.
if (pLcpSide->Work.MRRU > pLcpSide->Want.MRRU) {
pLcpSide->Work.MRRU = pLcpSide->Want.MRRU;
dwRetCode = CONFIG_NAK;
}
}
break;
default:
// If we do not recognize the option we CONFIG_REJ it.
dwRetCode = CONFIG_REJ;
break;
}
return dwRetCode;
}
/***************************************************************************\
* BuildOptionList
*
\***************************************************************************/
DWORD BuildOptionList(BYTE *pOptions, DWORD *pcbOptions,
LCP_OPTIONS *LcpOptions, DWORD Negotiate)
{
DWORD OptionType;
DWORD dwRetCode;
DWORD cbOptionLength = *pcbOptions;
DWORD dwResult;
for (OptionType = 1; OptionType <= LCP_OPTION_LIMIT; OptionType++) {
if (Negotiate & (1 << OptionType)) {
dwResult = MakeOption(LcpOptions, OptionType,
(PPP_OPTION*)pOptions, cbOptionLength);
if (dwResult != NO_ERROR) {
return dwResult;
}
cbOptionLength -= ((PPP_OPTION*)pOptions)->Length;
pOptions += ((PPP_OPTION*)pOptions)->Length;
}
}
*pcbOptions -= cbOptionLength;
return NO_ERROR;
}
/***************************************************************************\
* LcpMakeConfigResult
*
\***************************************************************************/
DWORD LcpMakeConfigResult(PPP_CONFIG *pRecvConfig, PPP_CONFIG *pSendConfig,
DWORD cbSendConfig, BOOL fRejectNaks)
{
DWORD dwDesired;
DWORD dwRetCode;
DWORD ResultType = CONFIG_ACK;
PPP_OPTION *pRecvOption = (PPP_OPTION*)pRecvConfig->Data;
PPP_OPTION *pSendOption = (PPP_OPTION*)pSendConfig->Data;
LONG lSendLength = cbSendConfig - PPP_CONFIG_HDR_LEN;
LONG lRecvLength = WireToHostFormat16(pRecvConfig->Length) - PPP_CONFIG_HDR_LEN;
// Clear negotiate mask
gLcp.Remote.Work.Negotiate = 0;
while (lRecvLength > 0) {
if (pRecvOption->Length == 0) {
return ERROR_PPP_INVALID_PACKET;
}
lRecvLength -= pRecvOption->Length;
if (lRecvLength < 0 ) {
return ERROR_PPP_INVALID_PACKET;
}
dwRetCode = CheckOption(&gLcp.Remote, pRecvOption, TRUE);
// If we were building an ACK and we got a NAK or reject OR
// we were building a NAK and we got a reject.
if ((ResultType == CONFIG_ACK && dwRetCode != CONFIG_ACK) ||
(ResultType == CONFIG_NAK && dwRetCode == CONFIG_REJ)) {
ResultType = dwRetCode;
pSendOption = (PPP_OPTION*)pSendConfig->Data;
lSendLength = cbSendConfig - PPP_CONFIG_HDR_LEN;
}
// Remember that we processed this option
if (dwRetCode != CONFIG_REJ && pRecvOption->Type <= LCP_OPTION_LIMIT) {
gLcp.Remote.Work.Negotiate |= (1 << pRecvOption->Type);
}
if (dwRetCode == ResultType) {
// If this option is to be rejected, simply copy the
// rejected option to the send buffer
if (dwRetCode == CONFIG_REJ ||
(dwRetCode == CONFIG_NAK && fRejectNaks)) {
CopyMemory(pSendOption, pRecvOption, pRecvOption->Length);
} else {
dwRetCode = MakeOption(&gLcp.Remote.Work, pRecvOption->Type,
pSendOption, lSendLength);
if (dwRetCode != NO_ERROR) {
return dwRetCode;
}
}
lSendLength -= pSendOption->Length;
pSendOption = (PPP_OPTION*)((BYTE*)pSendOption + pSendOption->Length);
}
pRecvOption = (PPP_OPTION*)((BYTE*)pRecvOption + pRecvOption->Length);
}
// If this was an NAK and we cannot send any more NAKS then we
// make this a REJECT packet
if (ResultType == CONFIG_NAK && fRejectNaks) {
pSendConfig->Code = CONFIG_REJ;
} else {
pSendConfig->Code = (BYTE)ResultType;
}
HostToWireFormat16((WORD)(cbSendConfig - lSendLength), pSendConfig->Length);
if (ResultType == CONFIG_NAK && gLcp.dwMagicNumberFailureCount > 3) {
return ERROR_PPP_LOOPBACK_DETECTED;
}
return NO_ERROR;
}
/***************************************************************************\
* LcpMakeConfigRequest
*
\***************************************************************************/
DWORD LcpMakeConfigRequest(PPP_CONFIG *pConfig, DWORD cbConfig)
{
DWORD dwRetCode;
cbConfig -= PPP_CONFIG_HDR_LEN;
dwRetCode = BuildOptionList(pConfig->Data, &cbConfig, &gLcp.Local.Work,
gLcp.Local.Work.Negotiate);
if (dwRetCode != NO_ERROR) {
return dwRetCode;
}
HostToWireFormat16((WORD)(cbConfig + PPP_CONFIG_HDR_LEN), pConfig->Length);
return NO_ERROR;
}
/***************************************************************************\
* LcpConfigAckReceived
*
\***************************************************************************/
DWORD LcpConfigAckReceived(PPP_CONFIG *pConfig)
{
DWORD dwRetCode;
BYTE ConfigReqSent[LCP_DEFAULT_MRU];
PPP_OPTION *pOption = (PPP_OPTION*)pConfig->Data;
DWORD cbConfigReqSent = sizeof(ConfigReqSent);
DWORD dwLength;
dwLength = WireToHostFormat16(pConfig->Length) - PPP_CONFIG_HDR_LEN;
// Get a copy of last request we sent
dwRetCode = BuildOptionList(ConfigReqSent, &cbConfigReqSent,
&gLcp.Local.Work, gLcp.Local.Work.Negotiate);
if (dwRetCode != NO_ERROR) {
return dwRetCode;
}
if (dwLength != cbConfigReqSent) {
return ERROR_PPP_INVALID_PACKET;
}
if (memcmp(ConfigReqSent, pConfig->Data, dwLength) != 0) {
return ERROR_PPP_INVALID_PACKET;
}
return NO_ERROR;
}
/***************************************************************************\
* LcpConfigNakReceived
*
\***************************************************************************/
DWORD LcpConfigNakReceived(PPP_CONFIG *pConfig)
{
LONG cbConfig = WireToHostFormat16(pConfig->Length) - PPP_CONFIG_HDR_LEN;
PPP_OPTION *pOption = (PPP_OPTION*)pConfig->Data;
DWORD dwLastOption = 0;
DWORD dwResult;
while (cbConfig > 0) {
if (pOption->Length == 0) {
return ERROR_PPP_INVALID_PACKET;
}
cbConfig -= pOption->Length;
if (cbConfig < 0) {
return ERROR_PPP_INVALID_PACKET;
}
// If this option wasn't requested, mark it as negotiable.
if (pOption->Type <= LCP_OPTION_LIMIT &&
(gLcp.Local.WillNegotiate & (1 << pOption->Type)) &&
!(gLcp.Local.Work.Negotiate & (1 << pOption->Type))) {
gLcp.Local.Work.Negotiate |= (1 << pOption->Type);
}
dwLastOption = pOption->Type;
dwResult = CheckOption(&gLcp.Local, pOption, FALSE);
if (dwResult == CONFIG_REJ && pOption->Type <= LCP_OPTION_LIMIT) {
gLcp.Local.Work.Negotiate &= ~(1 << pOption->Type);
}
pOption = (PPP_OPTION*)((BYTE*)pOption + pOption->Length);
}
return NO_ERROR;
}
/***************************************************************************\
* LcpConfigRejReceived
*
\***************************************************************************/
DWORD LcpConfigRejReceived(PPP_CONFIG *pConfig)
{
LONG cbConfig = WireToHostFormat16(pConfig->Length) - PPP_CONFIG_HDR_LEN;
PPP_OPTION *pOption = (PPP_OPTION*)pConfig->Data;
DWORD dwLastOption = 0;
DWORD dwResult;
BYTE ReqOption[LCP_DEFAULT_MRU];
while (cbConfig > 0) {
if (pOption->Length == 0) {
return ERROR_PPP_INVALID_PACKET;
}
cbConfig -= pOption->Length;
if (cbConfig < 0) {
return ERROR_PPP_INVALID_PACKET;
}
// Can't receive an option out of order or an option that wasn't
// requested.
if (pOption->Type <= LCP_OPTION_LIMIT &&
(pOption->Type < dwLastOption ||
!(gLcp.Local.Work.Negotiate & (1 << pOption->Type)))) {
return ERROR_PPP_INVALID_PACKET;
}
dwResult = MakeOption(&gLcp.Local.Work, pOption->Type,
(PPP_OPTION*)ReqOption, sizeof(ReqOption));
if (dwResult != NO_ERROR) {
return dwResult;
}
if (memcmp(ReqOption, pOption, pOption->Length) != 0) {
return ERROR_PPP_INVALID_PACKET;
}
dwLastOption = pOption->Type;
if (pOption->Type <= LCP_OPTION_LIMIT) {
gLcp.Local.Work.Negotiate &= ~(1 << pOption->Type);
}
pOption = (PPP_OPTION*)((BYTE*)pOption + pOption->Length);
}
return NO_ERROR;
}
/***************************************************************************\
* LcpThisLayerUp
*
\***************************************************************************/
DWORD LcpThisLayerUp(VOID)
{
if (gLcp.Local.Work.Negotiate & LCP_N_ACCM) {
gFraming.RecvAccm = gLcp.Local.Work.ACCM;
}
if (gLcp.Local.Work.Negotiate & LCP_N_PFC) {
gFraming.fRecvPfc = gLcp.Local.Work.PFC;
}
if (gLcp.Local.Work.Negotiate & LCP_N_ACFC) {
gFraming.fRecvAcfc = gLcp.Local.Work.ACFC;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_ACCM) {
gFraming.SendAccm = gLcp.Remote.Work.ACCM;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_PFC) {
gFraming.fSendPfc = gLcp.Remote.Work.PFC;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_ACFC) {
gFraming.fSendAcfc = gLcp.Remote.Work.ACFC;
}
return NO_ERROR;
}
/***************************************************************************\
* LcpThisLayerDown
*
\***************************************************************************/
DWORD LcpThisLayerDown(VOID)
{
if (gLcp.Local.Work.Negotiate & LCP_N_ACCM) {
gFraming.RecvAccm = gLcpDefault.ACCM;
}
if (gLcp.Local.Work.Negotiate & LCP_N_PFC) {
gFraming.fRecvPfc = gLcpDefault.PFC;
}
if (gLcp.Local.Work.Negotiate & LCP_N_ACFC) {
gFraming.fRecvAcfc = gLcpDefault.ACFC;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_ACCM) {
gFraming.SendAccm = gLcpDefault.ACCM;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_PFC) {
gFraming.fSendPfc = gLcpDefault.PFC;
}
if (gLcp.Remote.Work.Negotiate & LCP_N_ACFC) {
gFraming.fSendAcfc = gLcpDefault.ACFC;
}
return NO_ERROR;
}
/***************************************************************************\
* LcpEnd
*
\***************************************************************************/
DWORD LcpEnd(VOID)
{
if (gLcp.Local.Work.pAPData != NULL) {
Free(gLcp.Local.Work.pAPData);
}
if (gLcp.Remote.Work.pAPData != NULL) {
Free(gLcp.Remote.Work.pAPData);
}
return NO_ERROR;
}
/***************************************************************************\
* LcpBegin
*
\***************************************************************************/
DWORD LcpBegin(VOID)
{
CopyMemory(&gLcp.Local.Want, &gLcpDefault, sizeof(LCP_OPTIONS));
CopyMemory(&gLcp.Remote.Want, &gLcpDefault, sizeof(LCP_OPTIONS));
gLcp.Local.WillNegotiate = gLcpNegotiate;
gLcp.Remote.WillNegotiate = gLcpNegotiate;
gLcp.Remote.APsAvailable = LCP_AP_PAP;
gLcp.Local.Want.MagicNumber = GetMagicNumber();
gLcp.Remote.Want.MagicNumber = gLcp.Local.Want.MagicNumber + 1;
gLcp.Local.Want.ACCM = 0;
gLcp.Local.Want.PFC = TRUE;
gLcp.Local.Want.ACFC = TRUE;
gLcp.Remote.Want.ACCM = 0;
gLcp.Remote.Want.PFC = TRUE;
gLcp.Remote.Want.ACFC = TRUE;
gLcp.Local.Want.Negotiate = (LCP_N_MAGIC | LCP_N_ACCM | LCP_N_PFC | LCP_N_ACFC);
gLcp.Remote.Want.Negotiate = (LCP_N_MAGIC | LCP_N_ACCM | LCP_N_PFC | LCP_N_ACFC);
CopyMemory(&gLcp.Local.Work, &gLcp.Local.Want, sizeof(LCP_OPTIONS));
CopyMemory(&gLcp.Remote.Work, &gLcp.Remote.Want, sizeof(LCP_OPTIONS));
return NO_ERROR;
}
/***************************************************************************\
* GetLcpInfo
*
\***************************************************************************/
VOID GetLcpInfo(PPP_CP_INFO *pInfo)
{
ZeroMemory(pInfo, sizeof(PPP_CP_INFO));
pInfo->Protocol = PPP_LCP_PROTOCOL;
pInfo->Recognize = DISCARD_REQ;
pInfo->CP_Begin = LcpBegin;
pInfo->CP_End = LcpEnd;
pInfo->CP_ThisLayerDown = LcpThisLayerDown;
pInfo->CP_ThisLayerUp = LcpThisLayerUp;
pInfo->CP_ConfigNakReceived = LcpConfigNakReceived;
pInfo->CP_ConfigRejReceived = LcpConfigRejReceived;
pInfo->CP_ConfigAckReceived = LcpConfigAckReceived;
pInfo->CP_MakeConfigResult = LcpMakeConfigResult;
pInfo->CP_MakeConfigRequest = LcpMakeConfigRequest;
}