📄 lcp.c
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// This source code is licensed under Microsoft Shared Source License
// Version 1.0 for Windows CE.
// For a copy of the license visit http://go.microsoft.com/fwlink/?LinkId=3223.
//
// PPP Link Control Protocol (LCP)
// Include Files
#include "windows.h"
#include "cclib.h"
#include "memory.h"
#include "cxport.h"
#include "crypt.h"
#include "ndis.h"
#include "ndiswan.h"
// PPP Include Files
#include "protocol.h"
#include "ppp.h"
#include "mac.h"
#include "lcp.h"
#include "auth.h"
#include "ras.h"
#include "ncp.h"
#include "pppserver.h"
void lcpIdleDisconnectTimerStop(IN PLCPContext pContext);
void lcpIdleDisconnectTimerStart(IN PLCPContext pContext, IN DWORD DurationMs);
void lcpSetIdleDisconnectMs(IN PVOID context, IN DWORD dwIdleDisconnectMs);
void
pppLcpCloseCompleteCallback(
PLCPContext c_p)
//
// This function is called when the LCP FSM transitions to the closed state.
// It completes any pending close requests.
//
{
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: +pppLcpCloseCompleteCallback\n" )));
pppExecuteCompleteCallbacks(&c_p->pPendingCloseCompleteList);
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: -pppLcpCloseCompleteCallback\n" )));
}
void
pppLcp_GetLinkInfoCompleteCallback(
IN PLCPContext pContext,
IN NDIS_WAN_GET_LINK_INFO *pInfo,
IN NDIS_STATUS Status
)
//
// Called after the MAC layer indicates that it is up
// and we retrieve MAC settings.
//
{
pppSession_t *pSession = (pppSession_t *)(pContext->session);
OptionRequireLevel orlLocalMRU,
orlLocalAuth,
orlPFC,
orlACFC;
USHORT DefaultMTU;
DefaultMTU = PPP_DEFAULT_MTU;
if (_tcscmp(pSession->rasEntry.szDeviceType, RASDT_Vpn) == 0)
DefaultMTU = PPP_DEFAULT_VPN_MTU;
pContext->linkMaxSendPPPInfoFieldSize = DefaultMTU;
pContext->linkMaxRecvPPPInfoFieldSize = DefaultMTU;
if (Status == NDIS_STATUS_SUCCESS)
{
//
// The MaxSend/RecvFrameSizes returned by the miniport must be reduced
// by 2 bytes to account for the PPP protocol field in order to compute
// the maximum PPP info field size.
//
pContext->linkMaxSendPPPInfoFieldSize = (USHORT)pInfo->MaxSendFrameSize - 2;
pContext->linkMaxRecvPPPInfoFieldSize = (USHORT)pInfo->MaxRecvFrameSize - 2;
}
lcpOptionValueReset(pContext);
// If our desired MaxRecvFrameSize is not the default MRU (1500),
// then we want to negotiate this with the peer. That is, we
// want to tell our peer what size packets we can handle.
orlLocalMRU = ORL_Allowed;
if (pContext->local.MRU != PPP_DEFAULT_MTU)
orlLocalMRU = ORL_Wanted;
orlPFC = ORL_Unsupported;
if (pContext->FramingBits & PPP_COMPRESS_PROTOCOL_FIELD)
orlPFC = ORL_Wanted;
orlACFC = ORL_Unsupported;
if (pContext->FramingBits & PPP_COMPRESS_ADDRESS_CONTROL)
orlACFC = ORL_Wanted;
PppFsmOptionsSetORLs(pContext->pFsm,
LCP_OPT_MRU, orlLocalMRU, ORL_Allowed,
LCP_OPT_PFC, orlPFC, orlPFC,
LCP_OPT_ACFC, orlACFC, orlACFC,
-1);
if (pSession->bIsServer)
{
orlLocalAuth = ORL_Allowed;
if (PPPServerGetSessionAuthenticationRequired(pSession))
orlLocalAuth = ORL_Required;
PppFsmOptionsSetORLs(pContext->pFsm,
LCP_OPT_AUTH_PROTOCOL, orlLocalAuth, ORL_Unsupported,
-1);
}
if (pContext->bMACLayerUp)
PppFsmLowerLayerUp(pContext->pFsm);
}
void
pppLcp_LowerLayerUp(
IN PVOID context)
//
// This function will be called when the MAC layer is up
//
{
PLCPContext pContext = (PLCPContext)context;
pppSession_t *pSession = (pppSession_t *)(pContext->session);
WCHAR wszRegKey[MAX_PATH + 1];
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: +pppLcp_LowerLayerUp\n" )));
pContext->bMACLayerUp = TRUE;
//
// Get Mac parameters
//
pppMac_GetFramingInfo(pSession->macCntxt, &pContext->FramingBits, &pContext->DesiredACCM);
//
// Override the global miniport value with the per-line
// registry setting value if present.
//
wsprintf(&wszRegKey[0], L"Comm\\ppp\\Parms\\Line\\%s", pContext->session->rasEntry.szDeviceName);
(void) ReadRegistryValues(HKEY_LOCAL_MACHINE, wszRegKey,
L"ACCM", REG_DWORD, 0, &pContext->DesiredACCM, sizeof(DWORD),
NULL);
pppMac_GetLinkInfo(pSession->macCntxt, pppLcp_GetLinkInfoCompleteCallback, pContext);
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: -pppLcp_LowerLayerUp\n" )));
}
void
pppLcp_LowerLayerDown(
IN PVOID context)
//
// This function will be called when the auth layer is down
//
{
PLCPContext pContext = (PLCPContext)context;
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: +pppLcp_LowerLayerDown\n" )));
pContext->bMACLayerUp = FALSE;
PppFsmLowerLayerDown(pContext->pFsm);
if( pContext->pFsm->state == PFS_Initial )
{
// Complete any pending close requests
pppLcpCloseCompleteCallback(pContext);
}
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: -pppLcp_LowerLayerDown\n" )));
}
static DWORD
lcpSendPacket(
PVOID context,
USHORT ProtocolWord,
PBYTE pData,
DWORD cbData)
{
PLCPContext pContext = (PLCPContext)context;
pppSession_t *pSession = (pppSession_t *)(pContext->session);
DWORD dwResult;
dwResult = pppSendData(pSession, ProtocolWord, pData, cbData);
return dwResult;
}
static void
lcpConfigureMACLayer(
PLCPContext pContext )
//
// Called to configure the MAC layer with either:
// 1. parameters negotiated by LCP (if LCP is Opened), OR
// 2. default LCP settings (prior to LCP entering the Opened state)
//
{
pppSession_t *pSession = (pppSession_t *)(pContext->session);
NDIS_WAN_SET_LINK_INFO LinkInfo;
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: +lcpConfigureMACLayer\n" )));
memset ((char *)&LinkInfo, 0, sizeof(LinkInfo));
if (pContext->pFsm->state == PFS_Opened)
{
// Update MAC Settings with negotiated values
//
LinkInfo.MaxSendFrameSize = pContext->peer.MRU;
LinkInfo.SendACCM = pContext->peer.ACCM;
LinkInfo.SendFramingBits = PPP_FRAMING;
if( pContext->peer.bPFC)
LinkInfo.SendFramingBits |= PPP_COMPRESS_PROTOCOL_FIELD;
if( pContext->peer.bACFC)
LinkInfo.SendFramingBits |= PPP_COMPRESS_ADDRESS_CONTROL;
LinkInfo.MaxRecvFrameSize = pContext->local.MRU;
LinkInfo.RecvACCM = pContext->local.ACCM;
LinkInfo.RecvFramingBits = PPP_FRAMING;
if( pContext->local.bPFC)
LinkInfo.RecvFramingBits |= PPP_COMPRESS_PROTOCOL_FIELD;
if( pContext->local.bACFC)
LinkInfo.RecvFramingBits |= PPP_COMPRESS_ADDRESS_CONTROL;
}
else
{
//
// Prior to entering the Opened state, LCP default settings
// are in effect (regardless of what our current in-negotiation
// context values may be at the moment).
//
LinkInfo.MaxSendFrameSize = PPP_DEFAULT_MTU;
if (pContext->linkMaxSendPPPInfoFieldSize < PPP_DEFAULT_MTU)
LinkInfo.MaxSendFrameSize = pContext->linkMaxSendPPPInfoFieldSize;
LinkInfo.SendACCM = LCP_DEFAULT_ACCM;
LinkInfo.SendFramingBits = PPP_FRAMING;
LinkInfo.MaxRecvFrameSize = pContext->linkMaxRecvPPPInfoFieldSize;
LinkInfo.RecvACCM = LCP_DEFAULT_ACCM;
LinkInfo.RecvFramingBits = PPP_FRAMING;
}
// Set MAC Layer
pppMac_SetLink(pSession->macCntxt, &LinkInfo );
}
static DWORD
lcpUp(
PVOID context)
//
// This is called when the FSM enters the Opened state
//
{
PLCPContext pContext = (PLCPContext)context;
pppSession_t *pSession = (pppSession_t *)(pContext->session);
lcpConfigureMACLayer( pContext );
// Indicate to Session Manager and upper layer that we are UP.
// RAS state AllDevicesConnected - doesn't seem quite right
// but is best fit.
pppChangeOfState( pSession, RASCS_AllDevicesConnected, 0 );
// Inform higher layer (AUTH) that LCP is up
AuthLowerLayerUp(pSession->authCntxt);
// Start the Idle Disconnect Timer as appropriate
lcpSetIdleDisconnectMs(pContext, pContext->dwIdleDisconnectMs);
return NO_ERROR;
}
static DWORD
lcpDown(
PVOID context)
//
// This is called when the FSM leaves the Opened state
//
{
PLCPContext pContext = (PLCPContext)context;
pppSession_t *pSession = (pppSession_t *)(pContext->session);
lcpIdleDisconnectTimerStop(pContext);
lcpOptionValueReset(pContext);
// Set MAC layer settings to defaults
lcpConfigureMACLayer( pContext );
// Indicate to upper (auth) layer we are down
AuthLowerLayerDown(pSession->authCntxt);
return NO_ERROR;
}
static DWORD
lcpStarted(
PVOID context)
{
PLCPContext pContext = (PLCPContext)context;
DEBUGMSG( ZONE_LCP, ( TEXT( "PPP: LCP STARTED\n" )));
return NO_ERROR;
}
static DWORD
lcpFinished(
PVOID context)
//
// Called when the FSM enters the Closed or Stopped state from a higher state,
// or enters the Initial state from the Starting state.
//
{
PLCPContext pContext = (PLCPContext)context;
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: +lcpFinished\n" )));
//
// Take the MAC layer down
// Note that this will result in the RASCS_Disconnected state being
// entered when LINECALLSTATE_DISCONNECTED is indicated by the miniport.
//
pppMac_CallClose(pContext->session->macCntxt, NULL, NULL);
if( pContext->pFsm->state == PFS_Initial )
{
// Complete any pending close requests
pppLcpCloseCompleteCallback(pContext);
}
DEBUGMSG(ZONE_FUNCTION, (TEXT( "PPP: -lcpFinished\n" )));
return NO_ERROR;
}
DWORD
lcpSessionLock(
PVOID context)
//
// This is called by the FSM when it needs to lock the session
// because a timer has expired.
//
{
PLCPContext pContext = (PLCPContext)context;
pppLock(pContext->session);
return NO_ERROR;
}
DWORD
lcpSessionUnlock(
PVOID context)
//
// This is called by the FSM when it needs to unlock the session
// after a prior call to lock it due to timer expiry.
//
{
PLCPContext pContext = (PLCPContext)context;
pppUnLock(pContext->session);
return NO_ERROR;
}
void
LcpProtocolRejected(
IN PVOID context)
//
// Called when we receive a protocol reject packet from
// the peer in which they are rejecting the LCP protocol.
//
{
PLCPContext pContext = (PLCPContext)context;
//
// A protocol reject of an LCP packet is a major error, can't do
// anything without LCP!
//
DEBUGMSG(ZONE_ERROR, (TEXT("PPP: ERROR - LCP protocol REJECTED\n")));
PppFsmProtocolRejected( pContext->pFsm );
}
void
lcpRxProtocolReject(
IN PVOID context,
IN BYTE code,
IN BYTE id,
IN PBYTE pData,
IN DWORD cbData)
//
// Called when we receive an LCP_PROTOCOL_REJECT packet from
// the peer because they didn't like the protocol type in a
// packet we sent.
// pData will point to the packet that we sent that the peer
// doesn't like.
//
{
PLCPContext pContext = (PLCPContext)context;
pppMsg_t msg;
// At this point the LCP header has been removed from the received
// packet. Therefore this packet is our packet from the protocol
// to be terminated. Decode the packet again to determine the enclosed
// protocol and change the option header to P_PROT_REJ. Route the packet
// with the changed Code back to the protocol to terminate it.
msg.data = pData;
msg.len = cbData;
PPPRxProtocolReject(pContext->session, &msg);
}
void
lcpRxEchoRequest(
IN PVOID context,
IN BYTE code,
IN BYTE id,
IN PBYTE pData,
IN DWORD cbData)
{
PLCPContext pContext = (PLCPContext)context;
PBYTE pPacket = pData - PPP_PACKET_HEADER_SIZE;
if (cbData < 4)
{
// 4 byte magic number field is required in an echo-request or echo-reply
DEBUGMSG( ZONE_LCP, (TEXT( "PPP: LCP ECHO REQUEST missing magic number, cbData=%u\n"), cbData));
}
else
{
// Reception of a Magic-Number equal to the negotiated local magic number
// indicates a looped back link.
// Reception of a Magic-Number other than the negotiated local magic number,
// the peer's negotiated magic number, or zero if the peer didn't negotiate one,
// indicates a link which has been (mis)configured for communications with
// a different peer.
// Change packet code to echo REPLY
pPacket[0] = LCP_ECHO_REPLY;
// Put in our magic number
memcpy(pData, &pContext->local.MagicNumber, sizeof( DWORD ) );
// Send the REPLY
pppSendData(pContext->session, PPP_PROTOCOL_LCP, pPacket, cbData + PPP_PACKET_HEADER_SIZE);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
//
// Echo Request Management for dead link detection
//
// 1. If no packets of any kind are received from the peer for half a disconnect timeout interval
// (default 10/2 = 5 seconds), then we send an LCP echo request.
// 2. We keep sending LCP echo requests (at default 1 second intervals) until either we receive a packet
// (echo reply or something else) or the disconnect timeout interval is reached.
// 3. If no packets of any kind are received from the peer for a full disconnect timeout interval,
// then we terminate the link.
//
// By default dead link detection is only enabled for PPPoE connections. The specific device types
// for which it is enabled can be specified by setting the registry value:
// [HKEY_LOCAL_MACHINE\Comm\Ppp\Parms]
// "LcpIdleDeviceTypes"=multi_sz:"vpn","direct","pppoe"
//
void
lcpRxEchoReply(
IN PVOID context,
IN BYTE code,
IN BYTE id,
IN PBYTE pData,
IN DWORD cbData)
{
PLCPContext pContext = (PLCPContext)context;
//
// Let's not be picky about the ID matching that of the most recent request.
// The peer is there and sending us messages, so that's good.
//
// The session will have set the bRxData flag already to indicate that we
// have received data and thus the peer is alive, we don't need to do anything.
//
}
void
lcpEchoRequestSend(
PLCPContext pContext)
//
// Build and send an LCP Echo Request packet.
//
{
BYTE echoRequestPacket[8];
//
// Echo request format:
// Code = LCP_ECHO_REQUEST (9)
// Id = sequence number incremented for each echo request transmission
// Length = 0x0008
// Magic Number (4 bytes)
//
pContext->EchoRequestId++;
PPP_SET_PACKET_HEADER(echoRequestPacket, LCP_ECHO_REQUEST, (BYTE)pContext->EchoRequestId, sizeof(echoRequestPacket));
memcpy(&echoRequestPacket[4], &pContext->local.MagicNumber, sizeof(DWORD) );
DEBUGMSG(ZONE_TRACE, (L"PPP: TX LCP Echo-Request ID=%u", pContext->EchoRequestId));
pppSendData(pContext->session, PPP_PROTOCOL_LCP, echoRequestPacket, 8);
}
void lcpIdleDisconnectTimerCb(CTETimer *timer, PVOID context);
void
lcpIdleDisconnectTimerStop(
IN PLCPContext pContext)
{
CTEStopTimer(&pContext->IdleDisconnectTimer);
}
void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -