/*++ 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; }