diff --git a/src/tests/kits/net/ppp/headers/KPPPDefs.h b/src/tests/kits/net/ppp/headers/KPPPDefs.h index c006ec38b4..dc9fbe488b 100644 --- a/src/tests/kits/net/ppp/headers/KPPPDefs.h +++ b/src/tests/kits/net/ppp/headers/KPPPDefs.h @@ -13,6 +13,7 @@ #define PPP_DEVICE_KEY "Device" #define PPP_AUTHENTICATOR_KEY "Authenticator" #define PPP_PEER_AUTHENTICATOR_KEY "Peer-Authenticator" +#define PPP_MULTILINK_KEY "Multilink-Protocol" // settings values #define PPP_CLIENT_MODE_VALUE "Client" @@ -27,21 +28,26 @@ #define PPP_ERROR_BASE B_ERRORS_END -// return values for Receive methods in addition to B_ERROR and B_OK +// return values for Send()/Receive() methods in addition to B_ERROR and B_OK +// PPP_UNHANDLED is also used by PPPOptionHandler enum { // B_ERROR means that the packet is corrupted // B_OK means the packet was handled correctly - // return values for PPPProtocol and PPPEncapsulator + // return values for PPPProtocol and PPPEncapsulator (and PPPOptionHandler) PPP_UNHANDLED = PPP_ERROR_BASE, // The packet does not belong to this handler. // Do not delete the packet when you return this! + // For PPPOptionHandler: the item is unrecognized // return values of PPPInterface::Receive() PPP_DISCARDED, // packet was silently discarded PPP_REJECTED, // a protocol-reject + + PPP_NO_CONNECTION + // could not send a packet because device is not connected }; // module key types (used when loading a module) @@ -50,14 +56,15 @@ enum { PPP_DEVICE_TYPE, PPP_PROTOCOL_TYPE, PPP_AUTHENTICATOR_TYPE, - PPP_PEER_AUTHENTICATOR_TYPE + PPP_PEER_AUTHENTICATOR_TYPE, + PPP_MULTILINK_TYPE }; // protocol and encapsulator flags enum { PPP_NO_FLAGS = 0x00, PPP_ALWAYS_ALLOWED = 0x01, - // protocol may send/receive while we are authenticating + // protocol may send/receive in PPP_ESTABLISHMENT_PHASE PPP_NEEDS_DOWN = 0x02 // protocol needs a Down() in addition to a Reset() to // terminate the connection properly (losing the connection @@ -75,8 +82,6 @@ enum PPP_PHASE { // the normal protocols like IP (i.e., IPCP) // the following must not be used by protocols! - PPP_CTOR_DTOR_PHASE = -1, - // set on construction/destruction of the interface PPP_DOWN_PHASE = 0, PPP_TERMINATION_PHASE = 1, // this is the selected phase when we are GOING down diff --git a/src/tests/kits/net/ppp/headers/KPPPDevice.h b/src/tests/kits/net/ppp/headers/KPPPDevice.h index a2618f2578..1efca403db 100644 --- a/src/tests/kits/net/ppp/headers/KPPPDevice.h +++ b/src/tests/kits/net/ppp/headers/KPPPDevice.h @@ -34,11 +34,32 @@ class PPPDevice { bool IsUp() const { return fIsUp; } + // The biggest of the two tranfer rates will be set in ifnet. + // These methods should return default values when disconnected. + virtual uint32 InputTransferRate() const = 0; + virtual uint32 OutputTransferRate() const = 0; + + virtual uint32 CountOutputBytes() const = 0; + // how many bytes are waiting to be sent? + virtual status_t Send(mbuf *packet) = 0; + // this should enqueue the packet and return immediately + status_t PassToInterface(mbuf *packet); + // This will pass the packet to the interface's queue. + // Do not call Interface::ReceiveFromDevice directly + // if this can block a Send()! protected: - void SetUp(bool isUp); - // report up/down events + // Report that we are going up/down + // (from now on, the Up() process can be aborted). + // Abort if false is returned! + bool UpStarted(); + bool DownStarted(); + + // report up/down events + void UpEvent(); + void UpFailedEvent(); + void DownEvent(); protected: bool fIsUp; diff --git a/src/tests/kits/net/ppp/headers/KPPPFiniteStateMachine.h b/src/tests/kits/net/ppp/headers/KPPPFiniteStateMachine.h new file mode 100644 index 0000000000..343c8228b8 --- /dev/null +++ b/src/tests/kits/net/ppp/headers/KPPPFiniteStateMachine.h @@ -0,0 +1,127 @@ +#ifndef _K_PPP_FINITE_STATE_MACHINE__H +#define _K_PPP_FINITE_STATE_MACHINE__H + +#include "KPPPLCP.h" + +#include "Locker.h" + + +class PPPFiniteStateMachine { + friend class PPPInterface; + friend class PPPLCP; + + private: + // may only be constructed/destructed by PPPInterface + PPPFiniteStateMachine(PPPInterface& interface); + ~PPPFiniteStateMachine(); + + // copies are not allowed! + PPPFiniteStateMachine(const PPPFiniteStateMachine& copy); + PPPFiniteStateMachine& operator= (const PPPFiniteStateMachine& copy); + + public: + PPPInterface *Interface() const + { return fInterface; } + PPPLCP& LCP() const + { return Interface()->LCP(); } + + PPP_STATE State() const + { return fState; } + PPP_PHASE Phase() const + { return fPhase; } + + uint8 NextID(); + // return the next id for lcp_packets + + // public events + void AuthenticationRequested(); + void AuthenticationAccepted(const char *name); + void AuthenticationDenied(const char *name); + const char *AuthenticationName() const; + // returns NULL if not authenticated + PPP_AUTHENTICATION_STATUS AuthenticationStatus() const + { return fAuthenticationStatus; } + + void PeerAuthenticationRequested(); + void PeerAuthenticationAccepted(const char *name); + void PeerAuthenticationDenied(const char *name); + const char *PeerAuthenticationName() const; + // returns NULL if not authenticated + PPP_AUTHENTICATION_STATUS PeerAuthenticationStatus() const + { return fPeerAuthenticationStatus; } + + bool TLSNotify(); + bool TLFNotify(); + void UpFailedEvent(); + void UpEvent(); + void DownEvent(); + + private: + BLocker& Locker() + { return fLock; } + void LeaveConstructionPhase(); + void EnterDestructionPhase(); + + // private FiniteStateMachine methods + void NewState(PPP_STATE next); + + // private events + void OpenEvent(); + void CloseEvent(); + void TOGoodEvent(); + void TOBadEvent(); + void RCRGoodEvent(mbuf *packet); + void RCRBadEvent(mbuf *nak, mbuf *reject); + void RCAEvent(mbuf *packet); + void RCNEvent(mbuf *packet); + void RTREvent(mbuf *packet); + void RTAEvent(mbuf *packet); + void RUCEvent(mbuf *packet, uint16 protocol); + void RXJGoodEvent(mbuf *packet); + void RXJBadEvent(mbuf *packet); + void RXREvent(mbuf *packet); + + // general events (for Good/Bad events) + void TimerEvent(); + void RCREvent(mbuf *packet); + void RXJEvent(mbuf *packet); + + // actions + void IllegalEvent(PPP_EVENT event); + void ThisLayerUp(); + void ThisLayerDown(); + void ThisLayerStarted(); + void ThisLayerFinished(); + void InitializeRestartCount(); + void ZeroRestartCount(); + void SendConfigureRequest(); + void SendConfigureAck(mbuf *packet); + void SendConfigureNak(mbuf *packet); + void SendTerminateRequest(); + void SendTerminateAck(mbuf *request); + void SendCodeReject(mbuf *packet, uint16 protocol); + void SendEchoReply(mbuf *request); + + private: + PPPInterface *fInterface; + + PPP_PHASE fPhase; + PPP_STATE fState; + + vint32 fID; + + PPP_AUTHENTICATION_STATUS fAuthenticationStatus, + fPeerAuthenticationStatus; + int32 fAuthenticatorIndex, fPeerAuthenticatorIndex; + + BLocker fLock; + + // counters and timers + int32 fMaxRequest, fMaxTerminate, fMaxNak; + int32 fRequestCounter, fTerminateCounter, fNakCounter; + bigtime_t fTimeout; + // last time we sent a packet +}; + + +#endif diff --git a/src/tests/kits/net/ppp/headers/KPPPInterface.h b/src/tests/kits/net/ppp/headers/KPPPInterface.h index 4cfc246ed8..843db3249d 100644 --- a/src/tests/kits/net/ppp/headers/KPPPInterface.h +++ b/src/tests/kits/net/ppp/headers/KPPPInterface.h @@ -5,7 +5,7 @@ #include "KPPPDefs.h" -#include "KPPPFSM.h" +#include "KPPPFiniteStateMachine.h" #include "KPPPLCP.h" #include "List.h" @@ -24,7 +24,7 @@ class PPPInterface { PPPInterface& operator= (const PPPInterface& copy); public: - PPPInterface(driver_settings *settings); + PPPInterface(driver_settings *settings, PPPInterface *parent = NULL); ~PPPInterface(); status_t InitCheck() const; @@ -32,8 +32,10 @@ class PPPInterface { driver_settings* Settings() { return fSettings; } - PPPFSM& FSM() const - { return fFSM; } + PPPFiniteStateMachine& FiniteStateMachine() const + { return fFiniteStateMachine; } + PPPLCP& LCP() const + { return fLCP; } bool RegisterInterface(); // adds us to the manager module and @@ -69,11 +71,15 @@ class PPPInterface { PPPEncapsulator *EncapsulatorFor(uint16 protocol, PPPEncapsulator start = NULL) const; - bool AddOptionHandler(PPPOptionHandler *handler); - bool RemoveOptionHandler(PPPOptionHandler *handler); - int32 CountOptionHandlers() const - { return fOptionHandlers.CountItems(); } - PPPOptionHandler *OptionHandlerAt(int32 index) const; + // multilink methods + void AddChild(PPPInterface *child); + void RemoveChild(PPPInterface *child); + int32 CountChildren() const + { return fChildren.CountItems(); } + PPPInterface *Parent() const + { return fParent; } + bool IsMultilink() const + { return fIsMultilink; } void SetAutoRedial(bool autoredial = true); bool DoesAutoRedial() const @@ -87,9 +93,9 @@ class PPPInterface { { return fMode; } // client or server mode? PPP_STATE State() const - { return FSM().State(); } + { return FiniteStateMachine().State(); } PPP_PHASE Phase() const - { return FSM().Phase(); } + { return FiniteStateMachine().Phase(); } bool Up(); // in server mode Up() listens for an incoming connection @@ -97,7 +103,7 @@ class PPPInterface { bool IsUp() const; /* void EnableReports(PPP_REPORT type, thread_id thread, - PPP_REPORT_FLAGS flags); + int32 flags); void DisableReports(PPP_REPORT type, thread_id thread); bool DoesReport(PPP_REPORT type, thread_id thread); */ @@ -110,22 +116,31 @@ class PPPInterface { status_t SendToDevice(mbuf *packet, uint16 protocol); status_t ReceiveFromDevice(mbuf *packet); + // This is called by the receive-thread. + // Only call this if it does not block Send() or + // SendToDevice()! private: - PPPLCP& LCP() const - { return fLCP; } void CalculateMRU(); + // multilink methods + void SetParent(PPPInterface *parent) + { fParent = parent; } + // void Report(PPP_REPORT type, int32 code, void *data, int32 length); private: driver_parameter *fSettings; - PPPFSM fFSM; + PPPFiniteStateMachine fFiniteStateMachine; PPPLCP fLCP; ifnet *fIfnet; uint32 fLinkMTU, fMRU, fHeaderLength; + PPPInterface *fParent; + List fChildren; + bool fIsMultilink; + bool fAutoRedial, fDialOnDemand; vint32 fAccesing; @@ -134,9 +149,7 @@ class PPPInterface { PPPDevice *fDevice; PPPEncapsulator *fFirstEncapsulator; - List fProtocols; - List fOptionHandlers; List fModules; BLocker& fGeneralLock; diff --git a/src/tests/kits/net/ppp/headers/KPPPLCP.h b/src/tests/kits/net/ppp/headers/KPPPLCP.h index 7fa9b4b85c..8ed6a8d075 100644 --- a/src/tests/kits/net/ppp/headers/KPPPLCP.h +++ b/src/tests/kits/net/ppp/headers/KPPPLCP.h @@ -25,10 +25,24 @@ class PPPLCP : public PPPProtocol { PPPLCP& operator= (const PPPLCP& copy); public: + PPPFiniteStateMachine& FiniteStateMachine() const + { return Interface()->FiniteStateMachine(); } + bool AddOptionHandler(PPPOptionHandler *handler); + bool RemoveOptionHandler(PPPOptionHandler *handler); + int32 CountOptionHandlers() const + { return fOptionHandlers.CountItems(); } + PPPOptionHandler *OptionHandlerAt(int32 index) const; + + void SetTarget(PPPEncapsulator *target) + { fTarget = target; } + // if target != all packtes will be passed to the encapsulator + // instead of the interface/device private: + List fOptionHandlers; + PPPEncapsulator *fTarget; }; diff --git a/src/tests/kits/net/ppp/headers/KPPPModule.h b/src/tests/kits/net/ppp/headers/KPPPModule.h index 6c2eae977e..eec4cadc41 100644 --- a/src/tests/kits/net/ppp/headers/KPPPModule.h +++ b/src/tests/kits/net/ppp/headers/KPPPModule.h @@ -9,8 +9,11 @@ class PPPInterface; typedef struct ppp_module_info { module_info minfo; status_t (*control)(uint32 op, void *data, size_t length); - status_t (*add_to)(PPPInterface *interface, + status_t (*add_to)(PPPInterface *mainInterface, PPPInterface *subInterface, driver_parameter *settings, int32 type); + // multilink: handlers that must run on a real device + // should be added to subInterface while mainInterface + // handlers are used for the bundle of interfaces } ppp_module_info; diff --git a/src/tests/kits/net/ppp/headers/KPPPOptionHandler.h b/src/tests/kits/net/ppp/headers/KPPPOptionHandler.h index dd18e75d5a..1517733634 100644 --- a/src/tests/kits/net/ppp/headers/KPPPOptionHandler.h +++ b/src/tests/kits/net/ppp/headers/KPPPOptionHandler.h @@ -29,7 +29,7 @@ class PPPOptionHandler { // e.g.: remove list of rejected values // we want to send a configure request or we received a reply - virtual bool AddToRequest(PPPConfigurePacket *request) = 0; + virtual status_t AddToRequest(PPPConfigurePacket *request) = 0; virtual void ParseNak(const PPPConfigurePacket *nak) = 0; // create next request based on these and previous values virtual void ParseReject(const PPPConfigurePacket *reject) = 0; @@ -38,10 +38,12 @@ class PPPOptionHandler { // this is called for all handlers // peer sent configure request - virtual bool ParseConfigureRequest(const PPPConfigurePacket *request, + virtual status_t ParseRequest(const PPPConfigurePacket *request, int32 item, PPPConfigurePacket *nak, PPPConfigurePacket *reject) = 0; // item may be behind the last item which means we can add // additional values + virtual void SendingAck(const PPPConfigurePacket *ack) = 0; + // notification that we ack these values private: const char *fName; diff --git a/src/tests/kits/net/ppp/src/KPPPFiniteStateMachine.cpp b/src/tests/kits/net/ppp/src/KPPPFiniteStateMachine.cpp new file mode 100644 index 0000000000..31b391ec6c --- /dev/null +++ b/src/tests/kits/net/ppp/src/KPPPFiniteStateMachine.cpp @@ -0,0 +1,1071 @@ +#include "KPPPFiniteStateMachine.h" + + +// TODO: +// - add support for multilink interfaces +// (LCP packets may be received over MP encapsulators +// so we should return the reply using the same encapsulator) +// - add Up(Failed)/DownEvent for child interfaces + protocols + encapsulators + +PPPFiniteStateMachine::PPPFiniteStateMachine(PPPInterface& interface) + : fInterface(&interface), fPhase(PPP_DOWN_PHASE), + fState(PPP_INITIAL_STATE), fID(system_time() & 0xFF), + fAuthenticationStatus(PPP_NOT_AUTHENTICATED), + fPeerAuthenticationStatus(PPP_NOT_AUTHENTICATED), + fAuthenticatorIndex(-1), fPeerAuthenticatorIndex(-1), + fMaxTerminate(2), fMaxConfigure(10), fMaxNak(5), + fTimeout(0) +{ +} + + +PPPFiniteStateMachine::~PPPFiniteStateMachine() +{ +} + + +uint8 +PPPFiniteStateMachine::NextID() +{ + return (uint8) atomic_add(&fID, 1); +} + + +// remember: NewState() must always be called _after_ IllegalEvent() +// because IllegalEvent() also looks at the current state. +void +PPPFiniteStateMachine::NewState(PPP_STATE next) +{ +// if(State() == PPP_OPENED_STATE) +// ResetOptionHandlers(); + + fState = next; +} + + +// authentication events +void +PPPFiniteStateMachine::AuthenticationRequested() +{ + // IMPLEMENT ME! +} + + +void +PPPFiniteStateMachine::AuthenticationAccepted(const char *name) +{ + // IMPLEMENT ME! +} + + +void +PPPFiniteStateMachine::AuthenticationDenied(const char *name) +{ + // IMPLEMENT ME! +} + + +const char* +PPPFiniteStateMachine::AuthenticationName() const +{ + // IMPLEMENT ME! +} + + +void +PPPFiniteStateMachine::PeerAuthenticationRequested() +{ + // IMPLEMENT ME! +} + + +void +PPPFiniteStateMachine::PeerAuthenticationAccepted(const char *name) +{ + // IMPLEMENT ME! +} + + +void +PPPFiniteStateMachine::PeerAuthenticationDenied(const char *name) +{ + // IMPLEMENT ME! +} + + +const char* +PPPFiniteStateMachine::PeerAuthenticationName() const +{ + // IMPLEMENT ME! +} + + +// 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 +PPPFiniteStateMachine::TLSNotify() +{ + LockerHelper locker(fLock); + + if(State() == PPP_STARTING_STATE) { + if(Phase() == PPP_DOWN_PHASE) + fPhase = 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 +PPPFiniteStateMachine::TLFNotify() +{ + LockerHelper locker(fLock); + + if(Phase() == PPP_ESTABLISHMENT_PHASE) { + // we may not go down because an OpenEvent indicates that the + // user wants to connect + return false; + } + + // from now on no packets may be sent to the device + fPhase = PPP_DOWN_PHASE; + + return true; +} + + +void +PPPFiniteStateMachine::UpFailedEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + 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_FAILED_EVENT); + NewState(PPP_INITIAL_STATE); + break; + } + + // TODO: + // report that up failed + // Report(PPP_CONNECTION_REPORT, PPP_UP_FAILED, NULL, 0); + break; + + default: + IllegalEvent(PPP_UP_FAILED_EVENT); + } +} + + +void +PPPFiniteStateMachine::UpEvent() +{ + // 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(!Device()->IsUp()) + return; + // it is not our device that went up... + + switch(State()) { + case PPP_INITIAL_STATE: + if(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(); + } + + // TODO: handle server-up! + 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 +PPPFiniteStateMachine::DownEvent() +{ + LockerHelper locker(fLock); + + // TODO: + // ResetProtocols(); + // ResetEncapsulators(); + // ResetOptionHandlers(); + + switch(State()) { + case PPP_CLOSED_STATE: + case PPP_CLOSING_STATE: + NewState(PPP_INITIAL_STATE); + fPhase = PPP_DOWN_PHASE; + 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). + NewStatus(PPP_STARTING_STATE); + fPhase = PPP_DOWN_PHASE; + + // TODO: + // report that we lost the connection + // if fAuthStatus == FAILED || fPeerAuthStatus == FAILED + // Report(PPP_CONNECTION_REPORT, PPP_AUTH_FAILED, NULL, 0); + // else + // Report(PPP_CONNECTION_REPORT, PPP_CONNECTION_LOST, NULL, 0); + break; + + case PPP_STOPPING_STATE: + NewStatus(PPP_STARTING_STATE); + fPhase = PPP_DOWN_PHASE; + + // TODO: + // report that we lost the connection + // Report(PPP_CONNECTION_REPORT, PPP_CONNECTION_LOST, NULL, 0); + break; + + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewStatus(PPP_STARTING_STATE); + fPhase = PPP_DOWN_PHASE; + + // TODO: + // report that we lost the connection + // Report(PPP_CONNECTION_REPORT, PPP_CONNECTION_LOST, NULL, 0); + break; + + case PPP_OPENED_STATE: + // tld/1 + NewStatus(PPP_STARTING_STATE); + fPhase = PPP_DOWN_PHASE; + + // TODO: + // report that we lost the connection + // Report(PPP_CONNECTION_REPORT, PPP_CONNECTION_LOST, NULL, 0); + break; + + default: + IllegalEvent(PPP_DOWN_EVENT); + } + + // maybe we need to redial + if(State() == PPP_STARTING_STATE) { + if(Interface()->DoesAutoRedial()) { + // TODO: + // redial using a new thread (maybe interface manager will help us) + } + } +} + + +// private events +void +PPPFiniteStateMachine::OpenEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + NewState(PPP_STARTING_STATE); + 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); + fPhase = PPP_ESTABLISHMENT_PHASE; + InitializeRestartCount(); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + case PPP_CLOSING_STATE: + NewState(PPP_STOPPING_STATE); + } +} + + +void +PPPFiniteStateMachine::CloseEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_OPENED_STATE: + ThisLayerDown(); + + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_CLOSING_STATE); + fPhase = PPP_TERMINATION_PHASE; + InitializeRestartCount(); + locker.UnlockNow(); + SendTerminateRequest(); + break; + + case PPP_STARTING_STATE: + NewState(PPP_INITIAL_STATE); + + // if Device()->TLS() has not been called + // 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 + fPhase = 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; + } +} + + +// timeout (restart counters are > 0) +void +PPPFiniteStateMachine::TOGoodEvent() +{ + 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 +PPPFiniteStateMachine::TOBadEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_CLOSING_STATE: + NewState(PPP_CLOSED_STATE); + fPhase = 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); + fPhase = PPP_TERMINATION_PHASE; + locker.UnlockNow(); + ThisLayerFinished(); + break; + + default: + IllegalEvent(PPP_TO_BAD_EVENT); + } +} + + +// receive configure request (acceptable request) +void +PPPFiniteStateMachine::RCRGoodEvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RCR_GOOD_EVENT); + break; + + case PPP_CLOSED_STATE: + locker.UnlockNow(); + SendTerminateAck(); + break; + + case PPP_STOPPED_STATE: + // irc,scr,sca/8 + // XXX: should we do nothing and wait for DownEvent()? + 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: + // tld,scr,sca/8 + NewState(PPP_ACK_SENT_STATE); + locker.UnlockNow(); + SendConfigureRequest(); + SendConfigureAck(packet); + ThisLayerDown(); + break; + } +} + + +// receive configure request (unacceptable request) +void +PPPFiniteStateMachine::RCRBadEvent(mbuf *nak, mbuf *reject) +{ + 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); + 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 && mtod(nak, lcp_packet*)->length > 3) + SendConfigureNak(nak); + else if(!reject && mtod(reject, lcp_packet*)->length > 3) + SendConfigureNak(reject); + break; + } +} + + +// receive configure ack +void +PPPFiniteStateMachine::RCAEvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + PPPOptionHandler *handler; + PPPConfigurePacket ack(packet); + + for(int32 i = 0; i < LCP().CountOptionHandlers(); i++) { + handler = LCP().OptionHandlerAt(i); + handler->ParseAck(ack); + } + + 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); + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + } +} + + +// receive configure nak/reject +void +PPPFiniteStateMachine::RCNEvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + PPPOptionHandler *handler; + PPPConfigurePacket nak_reject(packet); + + for(int32 i = 0; i < LCP().CountOptionHandlers(); i++) { + handler = LCP().OptionHandlerAt(i); + + if(nak_reject.Code() == PPP_CONFIGURE_NAK) + handler->ParseNak(nak_reject); + else if(nak_reject.Code() == PPP_CONFIGURE_REJECT) + handler->ParseReject(nak_reject); + } + + 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(); + SendTermintateAck(packet); + 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); + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + } +} + + +// receive terminate request +void +PPPFiniteStateMachine::RTREvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RTR_EVENT); + break; + + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_REQ_SENT_STATE); + locker.UnlockNow(); + SendTerminateAck(packet); + break; + + case PPP_OPENED_STATE: + NewState(PPP_STOPPING_STATE); + ZeroRestartCount(); + locker.UnlockNow(); + ThisLayerDown(); + SendTerminateAck(packet); + break; + + default: + locker.UnlockNow(); + SendTerminateAck(packet); + } +} + + +// receive terminate ack +void +PPPFiniteStateMachine::RTAEvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + 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_CLOSING_STATE: + NewState(PPP_STOPPED_STATE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_ACK_RCVD_STATE: + NewState(REQ_SENT_STATE); + break; + + case PPP_OPENED_STATE: + NewState(PPP_REQ_SENT_STATE); + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + } +} + + +// receive unknown code +void +PPPFiniteStateMachine::RUCEvent(mbuf *packet, uint16 protocol) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RUC_EVENT); + break; + + default: + locker.UnlockNow(); + SendCodeReject(packet, protocol); + } +} + + +// receive code/protocol reject (acceptable such as IPX reject) +void +PPPFiniteStateMachine::RXJGoodEvent(mbuf *packet) +{ + 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; + } +} + + +// receive code/protocol reject (catastrophic such as LCP reject) +void +PPPFiniteStateMachine::RXJBadEvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RJX_BAD_EVENT); + break; + + case PPP_CLOSING_STATE: + NewState(PPP_CLOSED_STATE); + + case PPP_CLOSED_STATE: + 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); + + case PPP_STOPPED_STATE: + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_OPENED_STATE: + NewState(PPP_STOPPING_STATE); + InitializeRestartCount(); + locker.UnlockNow(); + ThisLayerDown(); + SendTerminateRequest(); + break; + } +} + + +// receive echo request/reply, discard request +void +PPPFiniteStateMachine::RXREvent(mbuf *packet) +{ + LockerHelper locker(fLock); + + lcp_packet *echo = mtod(mbuf, lcp_packet*); + + 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); + break; + } +} + + +// general events (for Good/Bad events) +void +PPPFiniteStateMachine::TimerEvent() +{ + // TODO: + // check which counter expired + // call TOGood/TOBad +} + + +// 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 +PPPFiniteStateMachine::RCREvent(mbuf *packet) +{ + PPPConfigurePacket request(packet), nak(PPP_CONFIGURE_NAK), + reject(PPP_CONFIGURE_REJECT); + PPPOptionHandler *handler; + + nak.SetID(request.ID()); + reject.SetID(request.ID()); + + // each handler should add unacceptable values for each item + bool handled; + int32 error; + for(int32 item = 0; item <= request.CountItems(); item++) { + // if we sent too many naks we should not append additional values + if(fNakCounter == 0 && item == request.CountItems()) + break; + + handled = false; + + for(int32 index = 0; index < LCP().CountOptionHandlers(); + index++) { + handler = LCP().OptionHandlerAt(i); + error = handler->ParseRequest(&request, item, &nak, &reject); + if(error == PPP_UNHANLED) + continue; + else if(error == B_ERROR) { + // the request contains a value that has been sent more than + // once or the value is corrupted + CloseEvent(); + return; + } else if(error = B_OK) + handled = true; + } + + if(!handled && item < request.CountItems()) { + // unhandled items should be added to reject + reject.AddItem(request.ItemAt(item)); + } + } + + if(nak.CountItems() > 0) + RCRBadEvent(nak.ToMbuf(), NULL); + else if(reject.CountItmes() > 0) + RCRBadEvent(NULL, reject.ToMbuf()); + else + RCRGoodEvent(packet); +} + + +// ReceiveCodeReject +// LCP received a code-reject packet and we look if it is acceptable. +// From here we call our Good/Bad counterparts. +void +PPPFiniteStateMachine::RXJEvent(mbuf *packet) +{ + lcp_packet *reject mtod(packet, lcp_packet*); + + if(reject->code == PPP_CODE_REJECT) + RXJBadEvent(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; + } + + int32 index; + PPPProtocol *protocol_handler; + PPPEncapsulator *encapsulator_handler = fFirstEncapsulator; + + for(index = 0; index < LCP().CountProtocols(); index++) { + protocol_handler = LCP().ProtocolAt(index); + if(protocol_handler && protocol_handler->Protocol() == rejected) + protocol_handler->SetEnabled(false); + // disable protocol + } + + for(; encapsulator_handler; + encapsulator_handler = encapsulator_handler->Next()) { + if(encapsulator_handler->Protocol() == rejected) + encapsulator_handler->SetEnabled(false); + // disable encapsulator + } + + RXJGoodEvent(packet); + } +} + + +// actions (all private) +void +PPPFiniteStateMachine::IllegalEvent(PPP_EVENT event) +{ + // TODO: + // update error statistics +} + + +void +PPPFiniteStateMachine::ThisLayerUp() +{ + // TODO: + // bring all P/Es up + // increase until we reach PPP_ESTABLISHED_PHASE or until we actually + // find P/Es that need an Up(). + // In this case we continue to increase the phase in UpEvent(P/E). +} + + +void +PPPFiniteStateMachine::ThisLayerDown() +{ + // TODO: + // DownProtocols(); + // DownEncapsulators(); + // ResetProtocols(); + // ResetEncapsulators(); + // If there are protocols/encapsulators with PPP_DOWN_NEEDED flag + // wait until they report a DownEvent() (or until all of them are down). +} + + +void +PPPFiniteStateMachine::ThisLayerStarted() +{ + if(Interface()->Device()) + Interface()->Device()->Up(); +} + + +void +PPPFiniteStateMachine::ThisLayerFinished() +{ + // TODO: + // ResetProtocols(); + // ResetEncapsulators(); + + if(Interface()->Device()) + Interface()->Device()->Down(); +} + + +void +PPPFiniteStateMachine::InitializeRestartCount() +{ + fConfigureCounter = fMaxConfigure; + fTerminateCounter = fMaxTerminate; + fNakCounter = fMaxNak; + + // TODO: + // start timer +} + + +void +PPPFiniteStateMachine::ZeroRestartCount() +{ + fConfigureCounter = 0; + fTerminateCounter = 0; + fNakCounter = 0; + + // TODO: + // start timer +} + + +void +PPPFiniteStateMachine::SendConfigureRequest() +{ + if(fConfigureCounter == 0) { + CloseEvent(); + return; + } + + --fConfigureCounter; + + PPPConfigurePacket request(PPP_CONFIGURE_REQUEST); + request.SetID(NextID()); + + for(int32 i = 0; i < LCP().CountOptionHandlers(); i++) { + // add all items + if(!LCP().OptionHandlerAt(i)->AddToRequest(&request)) { + CloseEvent(); + return; + } + } + + LCP().Send(request.ToMbuf()); + + fTimeout = system_time(); +} + + +void +PPPFiniteStateMachine::SendConfigureAck(mbuf *packet) +{ + mtod(packet, lcp_packet*)->code = PPP_CONFIGURE_ACK; + PPPConfigurePacket ack(packet); + + for(int32 i = 0; i < LCP().CountOptionHandlers; i++) + LCP().OptionHandlerAt(i)->SendingAck(&ack); + + LCP().Send(packet); +} + + +void +PPPFiniteStateMachine::SendConfigureNak(mbuf *packet) +{ + lcp_packet *nak = mtod(packet, 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; + } + + LCP().Send(packet); +} + + +void +PPPFiniteStateMachine::SendTerminateRequest() +{ +} + + +void +PPPFiniteStateMachine::SendTerminateAck(mbuf *request) +{ +} + + +void +PPPFiniteStateMachine::SendCodeReject(mbuf *packet, uint16 protocol) +{ +} + + +void +PPPFiniteStateMachine::SendEchoReply(mbuf *request) +{ +} diff --git a/src/tests/kits/net/ppp/src/KPPPInterface.cpp b/src/tests/kits/net/ppp/src/KPPPInterface.cpp index 65e22e99b5..e6ad18b325 100644 --- a/src/tests/kits/net/ppp/src/KPPPInterface.cpp +++ b/src/tests/kits/net/ppp/src/KPPPInterface.cpp @@ -23,12 +23,21 @@ // - maybe some protocols must go down instead of being reset -> add flag for this -PPPInterface::PPPInterface(driver_settings *settings) +PPPInterface::PPPInterface(driver_settings *settings, PPPInterface *parent = NULL) : fSettings(dup_driver_settings(settings)), - FSM(*this), LCP(*this), fIfnet(NULL), fLinkMTU(0), - fAccessing(0), fDevice(NULL), fFirstEncapsulator(NULL), - fGeneralLock(FSM().Locker()) + FiniteStateMachine(*this), LCP(*this), fIfnet(NULL), fLinkMTU(1500), + fAccessing(0), fChildrenCount(0), fDevice(NULL), fFirstEncapsulator(NULL), + fGeneralLock(FiniteStateMachine().Locker()) { + // are we a multilink subinterface? + if(parent && parent->IsMultilink()) { + fParent = parent; + fIsMultilink = true; + } else { + fParent = NULL; + fIsMultilink = false; + } + if(!fSettings) return; @@ -58,8 +67,6 @@ PPPInterface::PPPInterface(driver_settings *settings) // load all protocols and the device LoadModules(fSettings, 0, fSettings->parameter_count); - - FSM().LeaveConstructionPhase(); } @@ -69,9 +76,6 @@ PPPInterface::~PPPInterface() // remove our iface, so that nobody will access it: // go down if up // unregister from ppp_manager - - FSM().EnterDestructionPhase(); - // destroy and remove: // device // protocols @@ -85,8 +89,14 @@ PPPInterface::~PPPInterface() status_t PPPInterface::InitCheck() const { - if(!fDevice - || !fSettings) + if(!fSettings) + return B_ERROR; + + // sub-interfaces should have a device + if(IsMultilink()) { + if(Parent() && !fDevice) + return B_ERROR; + } else if(!fDevice) return B_ERROR; return B_OK; @@ -104,6 +114,10 @@ PPPInterface::RegisterInterface() return false; // we cannot register if something is wrong + // only MainInterfaces get an ifnet + if(IsMultilink() && Parent() && Parent()->RegisterInterface()) + return true; + ppp_manager_info *manager; if(get_module(PPP_MANAGER_MODULE_NAME, (module_info**) &manager) != B_OK) return false; @@ -127,6 +141,10 @@ PPPInterface::UnregisterInterface() return true; // we are already unregistered + // only MainInterfaces get an ifnet + if(IsMultilink() && Parent()) + return true; + ppp_manager_info *manager; if(get_module(PPP_MANAGER_MODULE_NAME, (module_info**) &manager) != B_OK) return false; @@ -143,8 +161,11 @@ PPPInterface::UnregisterInterface() void PPPInterface::SetLinkMTU(uint32 linkMTU) { - if(linkMTU < fLinkMTU && linkMTU > 234) - fLinkMTU = linkMTU; + LockerHelper locker(fLock); + + fLinkMTU = linkMTU; + + CalculateMRU(); } @@ -174,7 +195,13 @@ PPPInterface::SetDevice(PPPDevice *device) if(!device) return false; - if(Phase() != PPP_CTOR_DTOR_PHASE) + if(IsMultilink() && !Parent()) + return false; + // main interfaces do not have devices + + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -195,7 +222,9 @@ PPPInterface::AddProtocol(PPPProtocol *protocol) if(!protocol) return false; - if(Phase() != PPP_CTOR_DTOR_PHASE) + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -209,7 +238,9 @@ PPPInterface::AddProtocol(PPPProtocol *protocol) bool PPPInterface::RemoveProtocol(PPPProtocol *protocol) { - if(Phase() != PPP_CTOR_DTOR_PHASE) + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -258,7 +289,9 @@ PPPInterface::AddEncapsulator(PPPEncapsulator *encapsulator) if(!encapsulator) return false; - if(Phase() != PPP_CTOR_DTOR_PHASE) + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -300,7 +333,9 @@ PPPInterface::AddEncapsulator(PPPEncapsulator *encapsulator) bool PPPInterface::RemoveEncapsulator(PPPEncapsulator *encapsulator) { - if(Phase() != PPP_CTOR_DTOR_PHASE) + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -346,39 +381,34 @@ PPPInterface::EncapsulatorFor(uint16 protocol, bool -PPPInterface::AddOptionHandler(PPPOptionHandler *handler) +PPPInterface::AddChild(PPPInterface *child) { - if(!handler) - return false + LockerHelper locker(fLock); - if(Phase() != PPP_CTOR_DTOR_PHASE) + if(fChildren.HasItem(child) || !fChildren.AddItem(child)) return false; - // a running connection may not change - fOptionHandlers.AddItem(handler); + child->SetParent(this); + + CalculateMRU(); + + return true; } bool -PPPInterface::RemoveOptionHandler(PPPOptionHandler *handler) +PPPInterface::RemoveChild(PPPInterface *child) { - if(Phase() != PPP_CTOR_DTOR_PHASE) + LockerHelper locker(fLock); + + if(!fChildren.RemoveItem(child)) return false; - // a running connection may not change - return fOptionHandlers.RemoveItem(handler); -} - - -PPPOptionHandler* -PPPInterface::OptionHandlerAt(int32 index) const -{ - PPPOptionHandler *handler = fOptionHandlers.ItemAt(index); + child->SetParent(NULL); - if(handler == fOptionHandlers.DefaultItem()) - return NULL; + CalculateMRU(); - return handler; + return true; } @@ -419,7 +449,7 @@ PPPInterface::Up() // instead of waiting for state change we should wait until // all retries are done - if(!InitCheck() || Phase() != PPP_CTOR_DTOR_PHASE) + if(!InitCheck() || Phase() != PPP_DOWN_PHASE) return false; if(IsUp()) @@ -441,7 +471,7 @@ PPPInterface::Down() // instead of waiting for state change we should wait until // all retries are done - if(!InitCheck() || Phase() != PPP_CTOR_DTOR_PHASE) + if(!InitCheck()) return false; // TODO: @@ -455,6 +485,10 @@ bool PPPInterface::IsUp() const { LockerHelper locker(fGeneralLock); + + + + // set running flag if successful if(Ifnet()) return Ifnet()->if_flags & IFF_RUNNING; @@ -489,20 +523,44 @@ bool PPPInterface::LoadModules(const driver_settings *settings, int32 start, int32 count) { - if(Phase() != PPP_CTOR_DTOR_PHASE) + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change int32 type; // which type key was used for loading this module? + const char *name = NULL; + + // multilink handling + for(int32 i = start; + i < settings->parameter_count && i < start + count; + i++) { + if(!strcasecmp(settings->parameters[i].name, PPP_MULTILINK_KEY) + && settings->parameters[i].value_count > 0) { + if(!LoadModule(settings->parameters[i].value[0], + parameters[i].parameters, PPP_MULTILINK_TYPE)) + return false; + break; + } + + } + + // are we a multilink main interface? + if(IsMultilink() && !Parent()) { + // main interfaces only load the multilink module + // and create a child using their settings + AddChild(new PPPInterface(settings, this)); + return true; + } + for(int32 i = start; i < settings->parameter_count && i < start + count; i++) { type = -1; - const char *name = settings->parameters[i].name; + name = settings->parameters[i].name; if(!strcasecmp(name, PPP_LOAD_MODULE_KEY)) type = PPP_LOAD_MODULE_TYPE; @@ -531,7 +589,7 @@ bool PPPInterface::LoadModule(const char *name, const driver_parameter *parameter, int32 type) { - if(Phase() != PPP_CTOR_DTOR_PHASE) + if(Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change @@ -546,7 +604,7 @@ PPPInterface::LoadModule(const char *name, const driver_parameter *parameter, if(get_module(module_name, (module_info**) &module) != B_OK) return false; - if(!module->add_to(this, parameter, type)) + if(!module->add_to(Parent()?Parent():this, this, parameter, type)) return false; // add the module to the list of loaded modules @@ -619,16 +677,22 @@ PPPInterface::Receive(mbuf *packet, uint16 protocol) // the handler might be upped by this packet. PPPEncapsulator *encapsulator_handler = EncapsulatorFor(protocol); for(; encapsulator_handler; - encapsulator_handler = EncapsulatorFor(protocol, encapsulator_handler)) { + encapsulator_handler = EncapsulatorFor(protocol, encapsulator_handler->Next())) { if(!encapsulator_handler->IsEnabled()) { - // disabled handlers should not be used - result = PPP_REJECTED; + if(!encapsulator_handler->Next()) + break; + continue; + // disabled handlers should not be used } result = encapsulator_handler->Receive(packet, protocol); - if(result == PPP_UNHANDLED) + if(result == PPP_UNHANDLED) { + if(!encapsulator_handler->Next()) + break; + continue; + } return result; } @@ -653,14 +717,15 @@ PPPInterface::Receive(mbuf *packet, uint16 protocol) return result; } - // packet is unhandled - m_free(packet); + // maybe the parent interface can handle it + if(Parent()) + return Parent->Receive(packet, protocol); if(result == PPP_UNHANDLED) + m_free(packet); return PPP_DISCARDED; else { - // TODO: - // send protocol-reject! + FiniteStateMachine()->RUCEvent(packet, protocol); return PPP_REJECTED; } } @@ -675,7 +740,7 @@ PPPInterface::SendToDevice(mbuf *packet, uint16 protocol) return B_ERROR; // we must pass the basic tests like: - // do we have a device? + // do we have a device (as main interface)? // did we load all modules? if(InitCheck() != B_OK) { m_free(packet); @@ -689,8 +754,10 @@ PPPInterface::SendToDevice(mbuf *packet, uint16 protocol) } // go up if DialOnDemand enabled and we are down - if(DoesDialOnDemand() && Phase() == PPP_DOWN_PHASE) + if(DoesDialOnDemand() && (Phase() == PPP_DOWN_PHASE + || Phase() == PPP_ESTABLISHMENT_PHASE)) Up(); + // Up() waits until it is done // check if protocol is allowed and everything is up PPPProtocol *sending_protocol = ProtocolFor(protocol); @@ -712,20 +779,26 @@ PPPInterface::SendToDevice(mbuf *packet, uint16 protocol) if(packet == NULL) return B_ERROR; - // check if packet is too big - if((packet->m_flags & M_PKTHDR && packet->m_pkt_hdr.len > LinkMTU()) - || packet->m_len > LinkMTU()) { - m_free(packet); - return B_ERROR; - } - // set protocol (the only header field) protocol = htons(protocol); uint16 *header = mtod(packet, uint16*); *header = protocol; - // pass to device - return Device()->Send(packet); + // pass to device/children + if(!IsMultilink() || Parent()) { + // check if packet is too big for device + if((packet->m_flags & M_PKTHDR && packet->m_pkt_hdr.len > LinkMTU()) + || packet->m_len > LinkMTU()) { + m_free(packet); + return B_ERROR; + } + + return Device()->Send(packet); + } else { + // the multilink encapsulator should have sent it to some child interface + m_free(packet); + return B_ERROR; + } } @@ -755,7 +828,7 @@ PPPInterface::ReceiveFromDevice(mbuf *packet) if(!packet->m_pkthdr.rcvif) { m_free(packet); - return false; + return B_ERROR; } } @@ -779,6 +852,13 @@ PPPInterface::CalculateMRU() fMRU -= fHeaderLength; + if(Parent()) + Parent()->CalculateMRU(); + + for(int32 i = 0; i < fChildren.CountItems(); i++) + fMRU += fChildren.ItemAt(i)->MRU(); + // add our subinterfaces' MRU (so we have the MRRU) + if(Ifnet()) { Ifnet()->if_mtu = fMRU; Ifnet()->if_hdrlen = fHeaderLength; diff --git a/src/tests/kits/net/ppp/src/KPPPLCP.cpp b/src/tests/kits/net/ppp/src/KPPPLCP.cpp index 494e79ddc8..f952497e13 100644 --- a/src/tests/kits/net/ppp/src/KPPPLCP.cpp +++ b/src/tests/kits/net/ppp/src/KPPPLCP.cpp @@ -1,2 +1,62 @@ #include "KPPPLCP.h" + +// TODO: +// - add support for multilink interfaces +// (LCP packets may be received over MP encapsulators +// so we should return the reply using the same encapsulator) +// - add LCP extension handlers + + +PPPLCP::PPPLCP(PPPInterface& interface) + : PPPProtocol("LCP", PPP_ESTABLISHMENT_PHASE, PPP_LCP_PROTOCOL + , AF_UNSPEC, &interface, NULL, PPP_ALWAYS_ALLOWED), + fTarget(NULL) +{ +} + + +PPPLCP::~PPPLCP() +{ +} + + +bool +PPPLCP::AddOptionHandler(PPPOptionHandler *handler) +{ + if(!handler) + return false + + LockerHelper locker(FiniteStateMachine().Locker()); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + fOptionHandlers.AddItem(handler); +} + + +bool +PPPLCP::RemoveOptionHandler(PPPOptionHandler *handler) +{ + LockerHelper locker(FiniteStateMachine().Locker()); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + return fOptionHandlers.RemoveItem(handler); +} + + +PPPOptionHandler* +PPPLCP::OptionHandlerAt(int32 index) const +{ + PPPOptionHandler *handler = fOptionHandlers.ItemAt(index); + + if(handler == fOptionHandlers.DefaultItem()) + return NULL; + + return handler; +} diff --git a/src/tests/kits/net/ppp/src/settings_tools.h b/src/tests/kits/net/ppp/src/settings_tools.h index 0445fb4ade..7c95fbe9a3 100644 --- a/src/tests/kits/net/ppp/src/settings_tools.h +++ b/src/tests/kits/net/ppp/src/settings_tools.h @@ -1,6 +1,8 @@ #ifndef _SETTINGS_TOOLS__H #define _SETTINGS_TOOLS__H +// remove this as soon as we get the extended driver_settings API + struct driver_settings; struct driver_parameter; @@ -19,6 +21,8 @@ void add_parameter(const driver_parameter *from, driver_parameter *to) bool get_boolean_value(const char *string, bool unknownValue); const char *get_settings_value(const char *name, const driver_settings *settings); +const char *get_parameter_value(const char *name, const driver_parameter *parameters) + { get_settings_value(name, (driver_settings*) ¶meter->parameter_count); } #endif