Files
haiku-beta6/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPStateMachine.cpp
T
Waldemar Kornewald f9ad2df864 Added profile and interface naming support.
Added 'K' prefix to all kernel classes to resolve naming issue with doxygen.
Began some small doxygen comments.
Minor changes.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6282 a95241bf-73f2-0310-859d-f6bbb57e9c96
2004-01-25 12:54:00 +00:00

2040 lines
44 KiB
C++

//-----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//
// Copyright (c) 2003-2004 Waldemar Kornewald, [email protected]
//-----------------------------------------------------------------------
#include <OS.h>
#include <KPPPInterface.h>
#include <KPPPConfigurePacket.h>
#include <KPPPDevice.h>
#include <KPPPLCPExtension.h>
#include <KPPPOptionHandler.h>
#include <LockerHelper.h>
#include <net/if.h>
#include <core_funcs.h>
#define PPP_STATE_MACHINE_TIMEOUT 3000000
// 3 seconds
KPPPStateMachine::KPPPStateMachine(KPPPInterface& interface)
: fInterface(interface),
fLCP(interface.LCP()),
fState(PPP_INITIAL_STATE),
fPhase(PPP_DOWN_PHASE),
fID(system_time() & 0xFF),
fMagicNumber(0),
fLocalAuthenticationStatus(PPP_NOT_AUTHENTICATED),
fPeerAuthenticationStatus(PPP_NOT_AUTHENTICATED),
fLocalAuthenticationName(NULL),
fPeerAuthenticationName(NULL),
fMaxRequest(10),
fMaxTerminate(2),
fMaxNak(5),
fRequestID(0),
fTerminateID(0),
fEchoID(0),
fNextTimeout(0)
{
}
KPPPStateMachine::~KPPPStateMachine()
{
free(fLocalAuthenticationName);
free(fPeerAuthenticationName);
}
uint8
KPPPStateMachine::NextID()
{
return (uint8) atomic_add(&fID, 1);
}
// remember: NewState() must always be called _after_ IllegalEvent()
// because IllegalEvent() also looks at the current state.
void
KPPPStateMachine::NewState(ppp_state next)
{
#if DEBUG
dprintf("KPPPSM: NewState(%d) state=%d\n", next, State());
#endif
// maybe we do not need the timer anymore
if(next < PPP_CLOSING_STATE || next == PPP_OPENED_STATE)
fNextTimeout = 0;
if(State() == PPP_OPENED_STATE && next != State())
ResetLCPHandlers();
fState = next;
}
void
KPPPStateMachine::NewPhase(ppp_phase next)
{
#if DEBUG
if(next <= PPP_ESTABLISHMENT_PHASE || next == PPP_ESTABLISHED_PHASE)
dprintf("KPPPSM: NewPhase(%d) phase=%d\n", next, Phase());
#endif
// there is nothing after established phase and nothing before down phase
if(next > PPP_ESTABLISHED_PHASE)
next = PPP_ESTABLISHED_PHASE;
else if(next < PPP_DOWN_PHASE)
next = PPP_DOWN_PHASE;
// Report a down event to parent if we are not usable anymore.
// The report threads get their notification later.
if(Phase() == PPP_ESTABLISHED_PHASE && next != Phase()) {
if(Interface().Ifnet()) {
Interface().Ifnet()->if_flags &= ~IFF_RUNNING;
if(!Interface().DoesDialOnDemand())
Interface().Ifnet()->if_flags &= ~IFF_UP;
}
if(Interface().Parent())
Interface().Parent()->StateMachine().DownEvent(Interface());
}
fPhase = next;
if(Phase() == PPP_ESTABLISHED_PHASE) {
if(Interface().Ifnet())
Interface().Ifnet()->if_flags |= IFF_UP | IFF_RUNNING;
Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_UP_SUCCESSFUL,
&fInterface.fID, sizeof(ppp_interface_id));
}
}
// public actions
bool
KPPPStateMachine::Reconfigure()
{
#if DEBUG
dprintf("KPPPSM: Reconfigure() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(State() < PPP_REQ_SENT_STATE)
return false;
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
DownProtocols();
ResetLCPHandlers();
locker.UnlockNow();
return SendConfigureRequest();
}
bool
KPPPStateMachine::SendEchoRequest()
{
#if DEBUG
dprintf("KPPPSM: SendEchoRequest() state=%d phase=%d\n",
State(), Phase());
#endif
if(State() != PPP_OPENED_STATE)
return false;
struct mbuf *packet = m_gethdr(MT_DATA);
if(!packet)
return false;
packet->m_data += LCP().AdditionalOverhead();
packet->m_pkthdr.len = packet->m_len = 8;
// echo requests are at least eight bytes long
ppp_lcp_packet *request = mtod(packet, ppp_lcp_packet*);
request->code = PPP_ECHO_REQUEST;
request->id = NextID();
fEchoID = request->id;
request->length = htons(packet->m_len);
memcpy(request->data, &fMagicNumber, sizeof(fMagicNumber));
return LCP().Send(packet) == B_OK;
}
bool
KPPPStateMachine::SendDiscardRequest()
{
#if DEBUG
dprintf("KPPPSM: SendDiscardRequest() state=%d phase=%d\n",
State(), Phase());
#endif
if(State() != PPP_OPENED_STATE)
return false;
struct mbuf *packet = m_gethdr(MT_DATA);
if(!packet)
return false;
packet->m_data += LCP().AdditionalOverhead();
packet->m_pkthdr.len = packet->m_len = 8;
// discard requests are at least eight bytes long
ppp_lcp_packet *request = mtod(packet, ppp_lcp_packet*);
request->code = PPP_DISCARD_REQUEST;
request->id = NextID();
request->length = htons(packet->m_len);
memcpy(request->data, &fMagicNumber, sizeof(fMagicNumber));
return LCP().Send(packet) == B_OK;
}
// authentication events
void
KPPPStateMachine::LocalAuthenticationRequested()
{
#if DEBUG
dprintf("KPPPSM: LocalAuthenticationRequested() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fLocalAuthenticationStatus = PPP_AUTHENTICATING;
free(fLocalAuthenticationName);
fLocalAuthenticationName = NULL;
}
void
KPPPStateMachine::LocalAuthenticationAccepted(const char *name)
{
#if DEBUG
dprintf("KPPPSM: LocalAuthenticationAccepted() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fLocalAuthenticationStatus = PPP_AUTHENTICATION_SUCCESSFUL;
free(fLocalAuthenticationName);
if(name)
fLocalAuthenticationName = strdup(name);
else
fLocalAuthenticationName = NULL;
Interface().Report(PPP_CONNECTION_REPORT,
PPP_REPORT_LOCAL_AUTHENTICATION_SUCCESSFUL, &fInterface.fID,
sizeof(ppp_interface_id));
}
void
KPPPStateMachine::LocalAuthenticationDenied(const char *name)
{
#if DEBUG
dprintf("KPPPSM: LocalAuthenticationDenied() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fLocalAuthenticationStatus = PPP_AUTHENTICATION_FAILED;
free(fLocalAuthenticationName);
if(name)
fLocalAuthenticationName = strdup(name);
else
fLocalAuthenticationName = NULL;
}
void
KPPPStateMachine::PeerAuthenticationRequested()
{
#if DEBUG
dprintf("KPPPSM: PeerAuthenticationRequested() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fPeerAuthenticationStatus = PPP_AUTHENTICATING;
free(fPeerAuthenticationName);
fPeerAuthenticationName = NULL;
}
void
KPPPStateMachine::PeerAuthenticationAccepted(const char *name)
{
#if DEBUG
dprintf("KPPPSM: PeerAuthenticationAccepted() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fPeerAuthenticationStatus = PPP_AUTHENTICATION_SUCCESSFUL;
free(fPeerAuthenticationName);
if(name)
fPeerAuthenticationName = strdup(name);
else
fPeerAuthenticationName = NULL;
Interface().Report(PPP_CONNECTION_REPORT,
PPP_REPORT_PEER_AUTHENTICATION_SUCCESSFUL, &fInterface.fID,
sizeof(ppp_interface_id));
}
void
KPPPStateMachine::PeerAuthenticationDenied(const char *name)
{
#if DEBUG
dprintf("KPPPSM: PeerAuthenticationDenied() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
fPeerAuthenticationStatus = PPP_AUTHENTICATION_FAILED;
free(fPeerAuthenticationName);
if(name)
fPeerAuthenticationName = strdup(name);
else
fPeerAuthenticationName = NULL;
CloseEvent();
}
void
KPPPStateMachine::UpFailedEvent(KPPPInterface& interface)
{
#if DEBUG
dprintf("KPPPSM: UpFailedEvent(interface) state=%d phase=%d\n",
State(), Phase());
#endif
// TODO:
// log that an interface did not go up
}
void
KPPPStateMachine::UpEvent(KPPPInterface& interface)
{
#if DEBUG
dprintf("KPPPSM: UpEvent(interface) state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(Phase() <= PPP_TERMINATION_PHASE) {
interface.StateMachine().CloseEvent();
return;
}
Interface().CalculateBaudRate();
if(Phase() == PPP_ESTABLISHMENT_PHASE) {
// this is the first interface that went up
Interface().SetMRU(interface.MRU());
locker.UnlockNow();
ThisLayerUp();
} else if(Interface().MRU() > interface.MRU())
Interface().SetMRU(interface.MRU());
// MRU should always be the smallest value of all children
NewState(PPP_OPENED_STATE);
}
void
KPPPStateMachine::DownEvent(KPPPInterface& interface)
{
#if DEBUG
dprintf("KPPPSM: DownEvent(interface) state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
uint32 MRU = 0;
// the new MRU
Interface().CalculateBaudRate();
// when all children are down we should not be running
if(Interface().IsMultilink() && !Interface().Parent()) {
uint32 count = 0;
KPPPInterface *child;
for(int32 index = 0; index < Interface().CountChildren(); index++) {
child = Interface().ChildAt(index);
if(child && child->IsUp()) {
// set MRU to the smallest value of all children
if(MRU == 0)
MRU = child->MRU();
else if(MRU > child->MRU())
MRU = child->MRU();
++count;
}
}
if(MRU == 0)
Interface().SetMRU(1500);
else
Interface().SetMRU(MRU);
if(count == 0) {
locker.UnlockNow();
DownEvent();
}
}
}
void
KPPPStateMachine::UpFailedEvent(KPPPProtocol *protocol)
{
#if DEBUG
dprintf("KPPPSM: UpFailedEvent(protocol) state=%d phase=%d\n",
State(), Phase());
#endif
if((protocol->Flags() & PPP_NOT_IMPORTANT) == 0) {
if(Interface().Mode() == PPP_CLIENT_MODE) {
// pretend we lost connection
if(Interface().IsMultilink() && !Interface().Parent())
for(int32 index = 0; index < Interface().CountChildren(); index++)
Interface().ChildAt(index)->StateMachine().CloseEvent();
else if(Interface().Device())
Interface().Device()->Down();
else
CloseEvent();
// just to be on the secure side ;)
} else
CloseEvent();
}
}
void
KPPPStateMachine::UpEvent(KPPPProtocol *protocol)
{
#if DEBUG
dprintf("KPPPSM: UpEvent(protocol) state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(Phase() >= PPP_ESTABLISHMENT_PHASE)
BringProtocolsUp();
}
void
KPPPStateMachine::DownEvent(KPPPProtocol *protocol)
{
#if DEBUG
dprintf("KPPPSM: DownEvent(protocol) state=%d phase=%d\n",
State(), Phase());
#endif
}
// This is called by the device to tell us that it entered establishment
// phase. We can use Device::Down() to abort establishment until UpEvent()
// is called.
// The return value says if we are waiting for an UpEvent(). If false is
// returned the device should immediately abort its attempt to connect.
bool
KPPPStateMachine::TLSNotify()
{
#if DEBUG
dprintf("KPPPSM: TLSNotify() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(State() == PPP_STARTING_STATE) {
if(Phase() == PPP_DOWN_PHASE)
NewPhase(PPP_ESTABLISHMENT_PHASE);
// this says that the device is going up
return true;
}
return false;
}
// This is called by the device to tell us that it entered termination phase.
// A Device::Up() should wait until the device went down.
// If false is returned we want to stay connected, though we called
// Device::Down().
bool
KPPPStateMachine::TLFNotify()
{
#if DEBUG
dprintf("KPPPSM: TLFNotify() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
NewPhase(PPP_TERMINATION_PHASE);
// tell DownEvent() that it may create a connection-lost-report
return true;
}
void
KPPPStateMachine::UpFailedEvent()
{
#if DEBUG
dprintf("KPPPSM: UpFailedEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_STARTING_STATE:
Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_DEVICE_UP_FAILED,
&fInterface.fID, sizeof(ppp_interface_id));
if(Interface().Parent())
Interface().Parent()->StateMachine().UpFailedEvent(Interface());
NewPhase(PPP_DOWN_PHASE);
// tell DownEvent() that it should not create a connection-lost-report
DownEvent();
break;
default:
IllegalEvent(PPP_UP_FAILED_EVENT);
}
}
void
KPPPStateMachine::UpEvent()
{
#if DEBUG
dprintf("KPPPSM: UpEvent() state=%d phase=%d\n",
State(), Phase());
#endif
// This call is public, thus, it might not only be called by the device.
// We must recognize these attempts to fool us and handle them correctly.
LockerHelper locker(fLock);
if(!Interface().Device() || !Interface().Device()->IsUp())
return;
// it is not our device that went up...
Interface().CalculateBaudRate();
switch(State()) {
case PPP_INITIAL_STATE:
if(Interface().Mode() != PPP_SERVER_MODE
|| Phase() != PPP_ESTABLISHMENT_PHASE) {
// we are a client or we do not listen for an incoming
// connection, so this is an illegal event
IllegalEvent(PPP_UP_EVENT);
NewState(PPP_CLOSED_STATE);
locker.UnlockNow();
ThisLayerFinished();
return;
}
// TODO: handle server-up! (maybe already done correctly)
NewState(PPP_REQ_SENT_STATE);
InitializeRestartCount();
locker.UnlockNow();
SendConfigureRequest();
break;
case PPP_STARTING_STATE:
// we must have called TLS() which sets establishment phase
if(Phase() != PPP_ESTABLISHMENT_PHASE) {
// there must be a BUG in the device add-on or someone is trying to
// fool us (UpEvent() is public) as we did not request the device
// to go up
IllegalEvent(PPP_UP_EVENT);
NewState(PPP_CLOSED_STATE);
locker.UnlockNow();
ThisLayerFinished();
break;
}
NewState(PPP_REQ_SENT_STATE);
InitializeRestartCount();
locker.UnlockNow();
SendConfigureRequest();
break;
default:
IllegalEvent(PPP_UP_EVENT);
}
}
void
KPPPStateMachine::DownEvent()
{
#if DEBUG
dprintf("KPPPSM: DownEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(Interface().Device() && Interface().Device()->IsUp())
return;
// it is not our device that went up...
Interface().CalculateBaudRate();
// reset IdleSince
Interface().fIdleSince = 0;
switch(State()) {
// XXX: this does not belong to the standard, but may happen in our
// implementation
case PPP_STARTING_STATE:
break;
case PPP_CLOSED_STATE:
case PPP_CLOSING_STATE:
NewState(PPP_INITIAL_STATE);
break;
case PPP_STOPPED_STATE:
// The RFC says we should reconnect, but our implementation
// will only do this if auto-redial is enabled (only clients).
NewState(PPP_STARTING_STATE);
break;
case PPP_STOPPING_STATE:
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
case PPP_OPENED_STATE:
NewState(PPP_STARTING_STATE);
break;
default:
IllegalEvent(PPP_DOWN_EVENT);
}
ppp_phase oldPhase = Phase();
NewPhase(PPP_DOWN_PHASE);
DownProtocols();
fLocalAuthenticationStatus = PPP_NOT_AUTHENTICATED;
fPeerAuthenticationStatus = PPP_NOT_AUTHENTICATED;
// maybe we need to redial
if(State() == PPP_STARTING_STATE) {
bool needsRedial = false;
// we do not try to redial if authentication failed
if(fLocalAuthenticationStatus == PPP_AUTHENTICATION_FAILED
|| fLocalAuthenticationStatus == PPP_AUTHENTICATING)
Interface().Report(PPP_CONNECTION_REPORT,
PPP_REPORT_LOCAL_AUTHENTICATION_FAILED, &fInterface.fID,
sizeof(ppp_interface_id));
else if(fPeerAuthenticationStatus == PPP_AUTHENTICATION_FAILED
|| fPeerAuthenticationStatus == PPP_AUTHENTICATING)
Interface().Report(PPP_CONNECTION_REPORT,
PPP_REPORT_PEER_AUTHENTICATION_FAILED, &fInterface.fID,
sizeof(ppp_interface_id));
else {
// if we are going up and lost connection the redial attempt becomes
// a dial retry which is managed by the main thread in Interface::Up()
if(Interface().fUpThread == -1)
needsRedial = true;
// test if UpFailedEvent() was not called
if(oldPhase != PPP_DOWN_PHASE)
Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_CONNECTION_LOST,
&fInterface.fID, sizeof(ppp_interface_id));
}
if(Interface().Parent())
Interface().Parent()->StateMachine().UpFailedEvent(Interface());
NewState(PPP_INITIAL_STATE);
if(Interface().DoesAutoRedial()) {
if(needsRedial)
Interface().Redial(Interface().RedialDelay());
} else if(!Interface().DoesDialOnDemand())
Interface().Delete();
} else {
Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_DOWN_SUCCESSFUL,
&fInterface.fID, sizeof(ppp_interface_id));
if(!Interface().DoesDialOnDemand())
Interface().Delete();
}
}
// private events
void
KPPPStateMachine::OpenEvent()
{
#if DEBUG
dprintf("KPPPSM: OpenEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
// reset all handlers
if(Phase() != PPP_ESTABLISHED_PHASE) {
DownProtocols();
ResetLCPHandlers();
}
switch(State()) {
case PPP_INITIAL_STATE:
if(!Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_GOING_UP,
&fInterface.fID, sizeof(ppp_interface_id)))
return;
if(Interface().Mode() == PPP_SERVER_MODE) {
NewPhase(PPP_ESTABLISHMENT_PHASE);
if(Interface().Device() && !Interface().Device()->Up()) {
Interface().Device()->UpFailedEvent();
return;
}
} else
NewState(PPP_STARTING_STATE);
if(Interface().IsMultilink() && !Interface().Parent()) {
NewPhase(PPP_ESTABLISHMENT_PHASE);
for(int32 index = 0; index < Interface().CountChildren(); index++)
if(Interface().ChildAt(index)->Mode() == Interface().Mode())
Interface().ChildAt(index)->StateMachine().OpenEvent();
} else {
locker.UnlockNow();
ThisLayerStarted();
}
break;
case PPP_CLOSED_STATE:
if(Phase() == PPP_DOWN_PHASE) {
// the device is already going down
return;
}
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
InitializeRestartCount();
locker.UnlockNow();
SendConfigureRequest();
break;
case PPP_CLOSING_STATE:
NewState(PPP_STOPPING_STATE);
break;
default:
;
}
}
void
KPPPStateMachine::CloseEvent()
{
#if DEBUG
dprintf("KPPPSM: CloseEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(Interface().IsMultilink() && !Interface().Parent()) {
NewState(PPP_INITIAL_STATE);
if(Phase() != PPP_DOWN_PHASE)
NewPhase(PPP_TERMINATION_PHASE);
ThisLayerDown();
for(int32 index = 0; index < Interface().CountChildren(); index++)
Interface().ChildAt(index)->StateMachine().CloseEvent();
return;
}
switch(State()) {
case PPP_OPENED_STATE:
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_CLOSING_STATE);
NewPhase(PPP_TERMINATION_PHASE);
// indicates to handlers that we are terminating
InitializeRestartCount();
locker.UnlockNow();
if(State() == PPP_OPENED_STATE)
ThisLayerDown();
SendTerminateRequest();
break;
case PPP_STARTING_STATE:
NewState(PPP_INITIAL_STATE);
// TLSNotify() will know that we were faster because we
// are in PPP_INITIAL_STATE now
if(Phase() == PPP_ESTABLISHMENT_PHASE) {
// the device is already up
NewPhase(PPP_DOWN_PHASE);
// this says the following DownEvent() was not caused by
// a connection fault
locker.UnlockNow();
ThisLayerFinished();
}
break;
case PPP_STOPPING_STATE:
NewState(PPP_CLOSING_STATE);
break;
case PPP_STOPPED_STATE:
NewState(PPP_STOPPED_STATE);
break;
default:
;
}
}
// timeout (restart counters are > 0)
void
KPPPStateMachine::TOGoodEvent()
{
#if DEBUG
dprintf("KPPPSM: TOGoodEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_CLOSING_STATE:
case PPP_STOPPING_STATE:
locker.UnlockNow();
SendTerminateRequest();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
case PPP_REQ_SENT_STATE:
case PPP_ACK_SENT_STATE:
locker.UnlockNow();
SendConfigureRequest();
break;
default:
IllegalEvent(PPP_TO_GOOD_EVENT);
}
}
// timeout (restart counters are <= 0)
void
KPPPStateMachine::TOBadEvent()
{
#if DEBUG
dprintf("KPPPSM: TOBadEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_CLOSING_STATE:
NewState(PPP_CLOSED_STATE);
NewPhase(PPP_TERMINATION_PHASE);
locker.UnlockNow();
ThisLayerFinished();
break;
case PPP_STOPPING_STATE:
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_STOPPED_STATE);
NewPhase(PPP_TERMINATION_PHASE);
locker.UnlockNow();
ThisLayerFinished();
break;
default:
IllegalEvent(PPP_TO_BAD_EVENT);
}
}
// receive configure request (acceptable request)
void
KPPPStateMachine::RCRGoodEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RCRGoodEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RCR_GOOD_EVENT);
m_freem(packet);
break;
case PPP_CLOSED_STATE:
locker.UnlockNow();
SendTerminateAck();
m_freem(packet);
break;
case PPP_STOPPED_STATE:
// irc,scr,sca/8
// XXX: should we do nothing and wait for DownEvent()?
m_freem(packet);
break;
case PPP_REQ_SENT_STATE:
NewState(PPP_ACK_SENT_STATE);
case PPP_ACK_SENT_STATE:
locker.UnlockNow();
SendConfigureAck(packet);
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_OPENED_STATE);
locker.UnlockNow();
SendConfigureAck(packet);
ThisLayerUp();
break;
case PPP_OPENED_STATE:
NewState(PPP_ACK_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
locker.UnlockNow();
ThisLayerDown();
SendConfigureRequest();
SendConfigureAck(packet);
break;
default:
m_freem(packet);
}
}
// receive configure request (unacceptable request)
void
KPPPStateMachine::RCRBadEvent(struct mbuf *nak, struct mbuf *reject)
{
#if DEBUG
dprintf("KPPPSM: RCRBadEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RCR_BAD_EVENT);
break;
case PPP_CLOSED_STATE:
locker.UnlockNow();
SendTerminateAck();
break;
case PPP_STOPPED_STATE:
// irc,scr,scn/6
// XXX: should we do nothing and wait for DownEvent()?
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
locker.UnlockNow();
ThisLayerDown();
SendConfigureRequest();
case PPP_ACK_SENT_STATE:
if(State() == PPP_ACK_SENT_STATE)
NewState(PPP_REQ_SENT_STATE);
// OPENED_STATE might have set this already
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
locker.UnlockNow();
if(nak && ntohs(mtod(nak, ppp_lcp_packet*)->length) > 3)
SendConfigureNak(nak);
else if(reject && ntohs(mtod(reject, ppp_lcp_packet*)->length) > 3)
SendConfigureNak(reject);
return;
// prevents the nak/reject from being m_freem()'d
default:
;
}
if(nak)
m_freem(nak);
if(reject)
m_freem(reject);
}
// receive configure ack
void
KPPPStateMachine::RCAEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RCAEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(fRequestID != mtod(packet, ppp_lcp_packet*)->id) {
// this packet is not a reply to our request
// TODO:
// log this event
m_freem(packet);
return;
}
// let the option handlers parse this ack
KPPPConfigurePacket ack(packet);
KPPPOptionHandler *optionHandler;
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) {
optionHandler = LCP().OptionHandlerAt(index);
if(optionHandler->ParseAck(ack) != B_OK) {
m_freem(packet);
locker.UnlockNow();
CloseEvent();
return;
}
}
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RCA_EVENT);
break;
case PPP_CLOSED_STATE:
case PPP_STOPPED_STATE:
locker.UnlockNow();
SendTerminateAck();
break;
case PPP_REQ_SENT_STATE:
NewState(PPP_ACK_RCVD_STATE);
InitializeRestartCount();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
locker.UnlockNow();
SendConfigureRequest();
break;
case PPP_ACK_SENT_STATE:
NewState(PPP_OPENED_STATE);
InitializeRestartCount();
locker.UnlockNow();
ThisLayerUp();
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
locker.UnlockNow();
ThisLayerDown();
SendConfigureRequest();
break;
default:
;
}
m_freem(packet);
}
// receive configure nak/reject
void
KPPPStateMachine::RCNEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RCNEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(fRequestID != mtod(packet, ppp_lcp_packet*)->id) {
// this packet is not a reply to our request
// TODO:
// log this event
m_freem(packet);
return;
}
// let the option handlers parse this nak/reject
KPPPConfigurePacket nak_reject(packet);
KPPPOptionHandler *optionHandler;
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) {
optionHandler = LCP().OptionHandlerAt(index);
if(nak_reject.Code() == PPP_CONFIGURE_NAK) {
if(optionHandler->ParseNak(nak_reject) != B_OK) {
m_freem(packet);
locker.UnlockNow();
CloseEvent();
return;
}
} else if(nak_reject.Code() == PPP_CONFIGURE_REJECT) {
if(optionHandler->ParseReject(nak_reject) != B_OK) {
m_freem(packet);
locker.UnlockNow();
CloseEvent();
return;
}
}
}
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RCN_EVENT);
break;
case PPP_CLOSED_STATE:
case PPP_STOPPED_STATE:
locker.UnlockNow();
SendTerminateAck();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_SENT_STATE:
InitializeRestartCount();
case PPP_ACK_RCVD_STATE:
if(State() == PPP_ACK_RCVD_STATE)
NewState(PPP_REQ_SENT_STATE);
locker.UnlockNow();
SendConfigureRequest();
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
locker.UnlockNow();
ThisLayerDown();
SendConfigureRequest();
break;
default:
;
}
m_freem(packet);
}
// receive terminate request
void
KPPPStateMachine::RTREvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RTREvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
// we should not use the same ID as the peer
if(fID == mtod(packet, ppp_lcp_packet*)->id)
fID -= 128;
fLocalAuthenticationStatus = PPP_NOT_AUTHENTICATED;
fPeerAuthenticationStatus = PPP_NOT_AUTHENTICATED;
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RTR_EVENT);
m_freem(packet);
break;
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_TERMINATION_PHASE);
// indicates to handlers that we are terminating
locker.UnlockNow();
SendTerminateAck(packet);
break;
case PPP_OPENED_STATE:
NewState(PPP_STOPPING_STATE);
NewPhase(PPP_TERMINATION_PHASE);
// indicates to handlers that we are terminating
ZeroRestartCount();
locker.UnlockNow();
ThisLayerDown();
SendTerminateAck(packet);
break;
default:
NewPhase(PPP_TERMINATION_PHASE);
// indicates to handlers that we are terminating
locker.UnlockNow();
SendTerminateAck(packet);
}
}
// receive terminate ack
void
KPPPStateMachine::RTAEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RTAEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
if(fTerminateID != mtod(packet, ppp_lcp_packet*)->id) {
// this packet is not a reply to our request
// TODO:
// log this event
m_freem(packet);
return;
}
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RTA_EVENT);
break;
case PPP_CLOSING_STATE:
NewState(PPP_CLOSED_STATE);
locker.UnlockNow();
ThisLayerFinished();
break;
case PPP_STOPPING_STATE:
NewState(PPP_STOPPED_STATE);
locker.UnlockNow();
ThisLayerFinished();
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
break;
case PPP_OPENED_STATE:
NewState(PPP_REQ_SENT_STATE);
NewPhase(PPP_ESTABLISHMENT_PHASE);
// indicates to handlers that we are reconfiguring
locker.UnlockNow();
ThisLayerDown();
SendConfigureRequest();
break;
default:
;
}
m_freem(packet);
}
// receive unknown code
void
KPPPStateMachine::RUCEvent(struct mbuf *packet, uint16 protocolNumber,
uint8 code = PPP_PROTOCOL_REJECT)
{
#if DEBUG
dprintf("KPPPSM: RUCEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RUC_EVENT);
m_freem(packet);
break;
default:
locker.UnlockNow();
SendCodeReject(packet, protocolNumber, code);
}
}
// receive code/protocol reject (acceptable such as IPX reject)
void
KPPPStateMachine::RXJGoodEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RXJGoodEvent() state=%d phase=%d\n",
State(), Phase());
#endif
// This method does not m_freem(packet) because the acceptable rejects are
// also passed to the parent. RXJEvent() will m_freem(packet) when needed.
LockerHelper locker(fLock);
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RXJ_GOOD_EVENT);
break;
case PPP_ACK_RCVD_STATE:
NewState(PPP_REQ_SENT_STATE);
break;
default:
;
}
}
// receive code/protocol reject (catastrophic such as LCP reject)
void
KPPPStateMachine::RXJBadEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RXJBadEvent() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RXJ_BAD_EVENT);
break;
case PPP_CLOSING_STATE:
NewState(PPP_CLOSED_STATE);
case PPP_CLOSED_STATE:
locker.UnlockNow();
ThisLayerFinished();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
NewState(PPP_STOPPED_STATE);
case PPP_STOPPING_STATE:
NewPhase(PPP_TERMINATION_PHASE);
case PPP_STOPPED_STATE:
locker.UnlockNow();
ThisLayerFinished();
break;
case PPP_OPENED_STATE:
NewState(PPP_STOPPING_STATE);
NewPhase(PPP_TERMINATION_PHASE);
// indicates to handlers that we are terminating
InitializeRestartCount();
locker.UnlockNow();
ThisLayerDown();
SendTerminateRequest();
break;
}
m_freem(packet);
}
// receive echo request/reply, discard request
void
KPPPStateMachine::RXREvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RXREvent() state=%d phase=%d\n",
State(), Phase());
#endif
ppp_lcp_packet *echo = mtod(packet, ppp_lcp_packet*);
if(echo->code == PPP_ECHO_REPLY && echo->id != fEchoID) {
// TODO:
// log that we got a reply, but no request was sent
}
switch(State()) {
case PPP_INITIAL_STATE:
case PPP_STARTING_STATE:
IllegalEvent(PPP_RXR_EVENT);
break;
case PPP_OPENED_STATE:
if(echo->code == PPP_ECHO_REQUEST)
SendEchoReply(packet);
return;
// this prevents the packet from being freed
default:
;
}
m_freem(packet);
}
// general events (for Good/Bad events)
void
KPPPStateMachine::TimerEvent()
{
#if DEBUG
if(fNextTimeout != 0)
dprintf("KPPPSM: TimerEvent()\n");
#endif
LockerHelper locker(fLock);
if(fNextTimeout == 0 || fNextTimeout > system_time())
return;
fNextTimeout = 0;
locker.UnlockNow();
switch(State()) {
case PPP_CLOSING_STATE:
case PPP_STOPPING_STATE:
if(fTerminateCounter <= 0)
TOBadEvent();
else
TOGoodEvent();
break;
case PPP_REQ_SENT_STATE:
case PPP_ACK_RCVD_STATE:
case PPP_ACK_SENT_STATE:
if(fRequestCounter <= 0)
TOBadEvent();
else
TOGoodEvent();
break;
default:
;
}
}
// ReceiveConfigureRequest
// Here we get a configure-request packet from LCP and aks all OptionHandlers
// if its values are acceptable. From here we call our Good/Bad counterparts.
void
KPPPStateMachine::RCREvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RCREvent() state=%d phase=%d\n",
State(), Phase());
#endif
KPPPConfigurePacket request(packet);
KPPPConfigurePacket nak(PPP_CONFIGURE_NAK);
KPPPConfigurePacket reject(PPP_CONFIGURE_REJECT);
// we should not use the same id as the peer
if(fID == mtod(packet, ppp_lcp_packet*)->id)
fID -= 128;
nak.SetID(request.ID());
reject.SetID(request.ID());
// each handler should add unacceptable values for each item
status_t result;
// the return value of ParseRequest()
KPPPOptionHandler *optionHandler;
for(int32 index = 0; index < request.CountItems(); index++) {
optionHandler = LCP().OptionHandlerFor(request.ItemAt(index)->type);
if(!optionHandler || !optionHandler->IsEnabled()) {
dprintf("KPPPSM::RCREvent(): unknown type:%d\n", request.ItemAt(index)->type);
// unhandled items should be added to the reject
reject.AddItem(request.ItemAt(index));
continue;
}
#if DEBUG
dprintf("KPPPSM::RCREvent(): OH=%s\n", optionHandler->Name());
#endif
result = optionHandler->ParseRequest(request, index, nak, reject);
if(result == PPP_UNHANDLED) {
// unhandled items should be added to the reject
reject.AddItem(request.ItemAt(index));
continue;
} else if(result != B_OK) {
// the request contains a value that has been sent more than
// once or the value is corrupted
dprintf("KPPPSM::RCREvent(): OptionHandler returned parse error!\n");
m_freem(packet);
CloseEvent();
return;
}
}
// Additional values may be appended.
// If we sent too many naks we should not append additional values.
if(fNakCounter > 0) {
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) {
optionHandler = LCP().OptionHandlerAt(index);
if(optionHandler && optionHandler->IsEnabled()) {
result = optionHandler->ParseRequest(request, request.CountItems(),
nak, reject);
if(result != B_OK) {
// the request contains a value that has been sent more than
// once or the value is corrupted
dprintf("KPPPSM::RCREvent(): OptionHandler returned append error!\n");
m_freem(packet);
CloseEvent();
return;
}
}
}
}
if(nak.CountItems() > 0) {
RCRBadEvent(nak.ToMbuf(Interface().MRU(), LCP().AdditionalOverhead()), NULL);
m_freem(packet);
} else if(reject.CountItems() > 0) {
RCRBadEvent(NULL, reject.ToMbuf(Interface().MRU(), LCP().AdditionalOverhead()));
m_freem(packet);
} else
RCRGoodEvent(packet);
}
// ReceiveCodeReject
// LCP received a code/protocol-reject packet and we look if it is acceptable.
// From here we call our Good/Bad counterparts.
void
KPPPStateMachine::RXJEvent(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: RXJEvent() state=%d phase=%d\n",
State(), Phase());
#endif
ppp_lcp_packet *reject = mtod(packet, ppp_lcp_packet*);
if(reject->code == PPP_CODE_REJECT) {
uint8 rejectedCode = reject->data[0];
// test if the rejected code belongs to the minimum LCP requirements
if(rejectedCode >= PPP_MIN_LCP_CODE && rejectedCode <= PPP_MAX_LCP_CODE) {
if(Interface().IsMultilink() && !Interface().Parent()) {
// Main interfaces do not have states between STARTING and OPENED.
// An RXJBadEvent() would enter one of those states which is bad.
m_freem(packet);
CloseEvent();
} else
RXJBadEvent(packet);
return;
}
// find the LCP extension and disable it
KPPPLCPExtension *lcpExtension;
for(int32 index = 0; index < LCP().CountLCPExtensions(); index++) {
lcpExtension = LCP().LCPExtensionAt(index);
if(lcpExtension->Code() == rejectedCode)
lcpExtension->SetEnabled(false);
}
m_freem(packet);
} else if(reject->code == PPP_PROTOCOL_REJECT) {
// disable all handlers for rejected protocol type
uint16 rejected = *((uint16*) reject->data);
// rejected protocol number
if(rejected == PPP_LCP_PROTOCOL) {
// LCP must not be rejected!
RXJBadEvent(packet);
return;
}
// disable protocols with the rejected protocol number
KPPPProtocol *protocol = Interface().FirstProtocol();
for(; protocol; protocol = protocol->NextProtocol()) {
if(protocol->ProtocolNumber() == rejected)
protocol->SetEnabled(false);
// disable protocol
}
RXJGoodEvent(packet);
// this event handler does not m_freem(packet)!!!
// notify parent, too
if(Interface().Parent())
Interface().Parent()->StateMachine().RXJEvent(packet);
else
m_freem(packet);
}
}
// actions (all private)
void
KPPPStateMachine::IllegalEvent(ppp_event event)
{
// TODO:
// update error statistics
dprintf("KPPPSM: IllegalEvent(event=%d) state=%d phase=%d\n",
event, State(), Phase());
}
void
KPPPStateMachine::ThisLayerUp()
{
#if DEBUG
dprintf("KPPPSM: ThisLayerUp() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
// We begin with authentication phase and wait until each phase is done.
// We stop when we reach established phase.
// Do not forget to check if we are going down.
if(Phase() != PPP_ESTABLISHMENT_PHASE)
return;
NewPhase(PPP_AUTHENTICATION_PHASE);
locker.UnlockNow();
BringProtocolsUp();
}
void
KPPPStateMachine::ThisLayerDown()
{
#if DEBUG
dprintf("KPPPSM: ThisLayerDown() state=%d phase=%d\n",
State(), Phase());
#endif
// KPPPProtocol::Down() should block if needed.
DownProtocols();
}
void
KPPPStateMachine::ThisLayerStarted()
{
#if DEBUG
dprintf("KPPPSM: ThisLayerStarted() state=%d phase=%d\n",
State(), Phase());
#endif
if(Interface().Device() && !Interface().Device()->Up())
Interface().Device()->UpFailedEvent();
}
void
KPPPStateMachine::ThisLayerFinished()
{
#if DEBUG
dprintf("KPPPSM: ThisLayerFinished() state=%d phase=%d\n",
State(), Phase());
#endif
if(Interface().Device())
Interface().Device()->Down();
}
void
KPPPStateMachine::InitializeRestartCount()
{
fRequestCounter = fMaxRequest;
fTerminateCounter = fMaxTerminate;
fNakCounter = fMaxNak;
}
void
KPPPStateMachine::ZeroRestartCount()
{
fRequestCounter = 0;
fTerminateCounter = 0;
fNakCounter = 0;
}
bool
KPPPStateMachine::SendConfigureRequest()
{
#if DEBUG
dprintf("KPPPSM: SendConfigureRequest() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
--fRequestCounter;
fNextTimeout = system_time() + PPP_STATE_MACHINE_TIMEOUT;
locker.UnlockNow();
KPPPConfigurePacket request(PPP_CONFIGURE_REQUEST);
request.SetID(NextID());
fRequestID = request.ID();
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) {
// add all items
if(LCP().OptionHandlerAt(index)->AddToRequest(request) != B_OK) {
CloseEvent();
return false;
}
}
return LCP().Send(request.ToMbuf(Interface().MRU(),
LCP().AdditionalOverhead())) == B_OK;
}
bool
KPPPStateMachine::SendConfigureAck(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: SendConfigureAck() state=%d phase=%d\n",
State(), Phase());
#endif
if(!packet)
return false;
mtod(packet, ppp_lcp_packet*)->code = PPP_CONFIGURE_ACK;
KPPPConfigurePacket ack(packet);
// notify all option handlers that we are sending an ack for each value
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) {
if(LCP().OptionHandlerAt(index)->SendingAck(ack) != B_OK) {
m_freem(packet);
CloseEvent();
return false;
}
}
return LCP().Send(packet) == B_OK;
}
bool
KPPPStateMachine::SendConfigureNak(struct mbuf *packet)
{
#if DEBUG
dprintf("KPPPSM: SendConfigureNak() state=%d phase=%d\n",
State(), Phase());
#endif
if(!packet)
return false;
ppp_lcp_packet *nak = mtod(packet, ppp_lcp_packet*);
if(nak->code == PPP_CONFIGURE_NAK) {
if(fNakCounter == 0) {
// We sent enough naks. Let's try a reject.
nak->code = PPP_CONFIGURE_REJECT;
} else
--fNakCounter;
}
return LCP().Send(packet) == B_OK;
}
bool
KPPPStateMachine::SendTerminateRequest()
{
#if DEBUG
dprintf("KPPPSM: SendTerminateRequest() state=%d phase=%d\n",
State(), Phase());
#endif
LockerHelper locker(fLock);
--fTerminateCounter;
fNextTimeout = system_time() + PPP_STATE_MACHINE_TIMEOUT;
locker.UnlockNow();
struct mbuf *packet = m_gethdr(MT_DATA);
if(!packet)
return false;
packet->m_pkthdr.len = packet->m_len = 4;
// reserve some space for other protocols
packet->m_data += LCP().AdditionalOverhead();
ppp_lcp_packet *request = mtod(packet, ppp_lcp_packet*);
request->code = PPP_TERMINATE_REQUEST;
request->id = fTerminateID = NextID();
request->length = htons(4);
return LCP().Send(packet) == B_OK;
}
bool
KPPPStateMachine::SendTerminateAck(struct mbuf *request = NULL)
{
#if DEBUG
dprintf("KPPPSM: SendTerminateAck() state=%d phase=%d\n",
State(), Phase());
#endif
struct mbuf *reply = request;
ppp_lcp_packet *ack;
if(!reply) {
reply = m_gethdr(MT_DATA);
if(!reply)
return false;
reply->m_data += LCP().AdditionalOverhead();
reply->m_pkthdr.len = reply->m_len = 4;
ack = mtod(reply, ppp_lcp_packet*);
ack->id = NextID();
} else
ack = mtod(reply, ppp_lcp_packet*);
ack->code = PPP_TERMINATE_ACK;
ack->length = htons(4);
return LCP().Send(reply) == B_OK;
}
bool
KPPPStateMachine::SendCodeReject(struct mbuf *packet, uint16 protocolNumber, uint8 code)
{
#if DEBUG
dprintf("KPPPSM: SendCodeReject(protocolNumber=%X;code=%d) state=%d phase=%d\n",
protocolNumber, code, State(), Phase());
#endif
if(!packet)
return false;
int32 length;
// additional space needed for this reject
if(code == PPP_PROTOCOL_REJECT)
length = 6;
else
length = 4;
M_PREPEND(packet, length);
// add some space for the header
// adjust packet if too big
int32 adjust = Interface().MRU();
if(packet->m_flags & M_PKTHDR) {
adjust -= packet->m_pkthdr.len;
} else
adjust -= packet->m_len;
if(adjust < 0)
m_adj(packet, adjust);
ppp_lcp_packet *reject = mtod(packet, ppp_lcp_packet*);
reject->code = code;
reject->id = NextID();
if(packet->m_flags & M_PKTHDR)
reject->length = htons(packet->m_pkthdr.len);
else
reject->length = htons(packet->m_len);
protocolNumber = htons(protocolNumber);
if(code == PPP_PROTOCOL_REJECT)
memcpy(&reject->data, &protocolNumber, sizeof(protocolNumber));
return LCP().Send(packet) == B_OK;
}
bool
KPPPStateMachine::SendEchoReply(struct mbuf *request)
{
#if DEBUG
dprintf("KPPPSM: SendEchoReply() state=%d phase=%d\n",
State(), Phase());
#endif
if(!request)
return false;
ppp_lcp_packet *reply = mtod(request, ppp_lcp_packet*);
reply->code = PPP_ECHO_REPLY;
// the request becomes a reply
if(request->m_flags & M_PKTHDR)
request->m_pkthdr.len = 8;
request->m_len = 8;
memcpy(reply->data, &fMagicNumber, sizeof(fMagicNumber));
return LCP().Send(request) == B_OK;
}
// methods for bringing protocols up
void
KPPPStateMachine::BringProtocolsUp()
{
// use a simple check for phase changes (e.g., caused by CloseEvent())
while(Phase() <= PPP_ESTABLISHED_PHASE && Phase() >= PPP_AUTHENTICATION_PHASE) {
if(BringPhaseUp() > 0)
break;
LockerHelper locker(fLock);
if(Phase() < PPP_AUTHENTICATION_PHASE)
return;
// phase was changed by another event
else if(Phase() == PPP_ESTABLISHED_PHASE) {
if(Interface().Parent())
Interface().Parent()->StateMachine().UpEvent(Interface());
break;
} else
NewPhase((ppp_phase) (Phase() + 1));
}
}
// this returns the number of handlers waiting to go up
uint32
KPPPStateMachine::BringPhaseUp()
{
// Servers do not need to bring all protocols up.
// The client specifies which protocols he wants to go up.
LockerHelper locker(fLock);
// check for phase change
if(Phase() < PPP_AUTHENTICATION_PHASE)
return 0;
uint32 count = 0;
KPPPProtocol *protocol = Interface().FirstProtocol();
for(; protocol; protocol = protocol->NextProtocol()) {
if(protocol->IsEnabled() && protocol->ActivationPhase() == Phase()) {
if(protocol->IsGoingUp() && Interface().Mode() == PPP_CLIENT_MODE)
++count;
else if(protocol->IsDown() && protocol->IsUpRequested()) {
if(Interface().Mode() == PPP_CLIENT_MODE)
++count;
protocol->Up();
}
}
}
// We only wait until authentication is complete.
if(Interface().Mode() == PPP_SERVER_MODE
&& (LocalAuthenticationStatus() == PPP_AUTHENTICATING
|| PeerAuthenticationStatus() == PPP_AUTHENTICATING))
++count;
return count;
}
void
KPPPStateMachine::DownProtocols()
{
KPPPProtocol *protocol = Interface().FirstProtocol();
for(; protocol; protocol = protocol->NextProtocol())
if(protocol->IsEnabled())
protocol->Down();
}
void
KPPPStateMachine::ResetLCPHandlers()
{
for(int32 index = 0; index < LCP().CountOptionHandlers(); index++)
LCP().OptionHandlerAt(index)->Reset();
for(int32 index = 0; index < LCP().CountLCPExtensions(); index++)
LCP().LCPExtensionAt(index)->Reset();
}