From 7aa79f33d8e92fae73ee96b2921117fba47abbda Mon Sep 17 00:00:00 2001 From: Waldemar Kornewald Date: Thu, 17 Jul 2003 16:46:08 +0000 Subject: [PATCH] Moved OptionHandlers to LCP (where they actually belong). Added some initial code to get general (as far as possible) multilink support (the modules must be aware of whether they belong to the device or to the bundle). PPPDevice now reports speed and number of bytes in the output queue. Removed PPP_CTOR_DTOR_PHASE. Modules can be loaded in PPP_DOWN_PHASE. Worked on events and actions. Some minor changes/fixes. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@4008 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/tests/kits/net/ppp/headers/KPPPDefs.h | 17 +- src/tests/kits/net/ppp/headers/KPPPDevice.h | 25 +- .../net/ppp/headers/KPPPFiniteStateMachine.h | 127 ++ .../kits/net/ppp/headers/KPPPInterface.h | 47 +- src/tests/kits/net/ppp/headers/KPPPLCP.h | 14 + src/tests/kits/net/ppp/headers/KPPPModule.h | 5 +- .../kits/net/ppp/headers/KPPPOptionHandler.h | 6 +- .../net/ppp/src/KPPPFiniteStateMachine.cpp | 1071 +++++++++++++++++ src/tests/kits/net/ppp/src/KPPPInterface.cpp | 208 +++- src/tests/kits/net/ppp/src/KPPPLCP.cpp | 60 + src/tests/kits/net/ppp/src/settings_tools.h | 4 + 11 files changed, 1492 insertions(+), 92 deletions(-) create mode 100644 src/tests/kits/net/ppp/headers/KPPPFiniteStateMachine.h create mode 100644 src/tests/kits/net/ppp/src/KPPPFiniteStateMachine.cpp 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