diff --git a/src/tests/kits/net/ppp/headers/KPPPDefs.h b/src/tests/kits/net/ppp/headers/KPPPDefs.h index dc9fbe488b..39c4f2fa3f 100644 --- a/src/tests/kits/net/ppp/headers/KPPPDefs.h +++ b/src/tests/kits/net/ppp/headers/KPPPDefs.h @@ -65,10 +65,12 @@ enum { PPP_NO_FLAGS = 0x00, PPP_ALWAYS_ALLOWED = 0x01, // protocol may send/receive in PPP_ESTABLISHMENT_PHASE - PPP_NEEDS_DOWN = 0x02 + PPP_NEEDS_DOWN = 0x02, // protocol needs a Down() in addition to a Reset() to // terminate the connection properly (losing the connection // still results in a Reset() only) + PPP_NOT_IMPORTANT = 0x03 + // if this protocol fails to go up we do not disconnect }; // phase when the protocol is brought up diff --git a/src/tests/kits/net/ppp/headers/KPPPDevice.h b/src/tests/kits/net/ppp/headers/KPPPDevice.h index 3e4ce0d4a4..1f1d80990c 100644 --- a/src/tests/kits/net/ppp/headers/KPPPDevice.h +++ b/src/tests/kits/net/ppp/headers/KPPPDevice.h @@ -29,6 +29,7 @@ class PPPDevice { { return fMTU; } virtual uint32 PreferredMTU() const = 0; + // these calls must not block virtual void Up() = 0; virtual void Down() = 0; virtual void Listen() = 0; diff --git a/src/tests/kits/net/ppp/headers/KPPPInterface.h b/src/tests/kits/net/ppp/headers/KPPPInterface.h index b40c7d8815..c9f14e1c8a 100644 --- a/src/tests/kits/net/ppp/headers/KPPPInterface.h +++ b/src/tests/kits/net/ppp/headers/KPPPInterface.h @@ -19,6 +19,8 @@ class PPPOptionHandler; class PPPInterface { + friend class PPPStateMachine; + private: // copies are not allowed! PPPInterface(const PPPInterface& copy); @@ -136,6 +138,7 @@ class PPPInterface { PPPLCP fLCP; PPPReportManager fReportManager; ifnet *fIfnet; + thread_id fUpThread; ppp_manager_info *fManager; diff --git a/src/tests/kits/net/ppp/headers/KPPPReportDefs.h b/src/tests/kits/net/ppp/headers/KPPPReportDefs.h index 0846158b0e..99fc3a944f 100644 --- a/src/tests/kits/net/ppp/headers/KPPPReportDefs.h +++ b/src/tests/kits/net/ppp/headers/KPPPReportDefs.h @@ -11,7 +11,8 @@ enum PPP_REPORT_FLAGS { PPP_NO_REPORT_FLAGS = 0, PPP_WAIT_FOR_REPLY = 0x1, - PPP_REMOVE_AFTER_REPORT = 0x2 + PPP_REMOVE_AFTER_REPORT = 0x2, + PPP_NO_REPLY_TIMEOUT = 0x4 }; // report types @@ -23,6 +24,7 @@ enum PPP_REPORT_TYPE { // report codes (type-specific) enum PPP_CONNECTION_REPORT_CODES { + PPP_GOING_UP = 0, PPP_UP_SUCCESSFUL = 1, PPP_DOWN_SUCCESSFUL = 2, PPP_UP_ABORTED = 3, diff --git a/src/tests/kits/net/ppp/headers/KPPPReportManager.h b/src/tests/kits/net/ppp/headers/KPPPReportManager.h index 0321d69f9b..7bdc37c1fa 100644 --- a/src/tests/kits/net/ppp/headers/KPPPReportManager.h +++ b/src/tests/kits/net/ppp/headers/KPPPReportManager.h @@ -7,6 +7,8 @@ #include "LockerHelper.h" +#define PPP_REPLY(sender, value) send_data_with_timeout((sender), (value), NULL, 0, B_REPORT_TIMEOUT) + class PPPReportManager { public: PPPReportManager(BLocker& lock); diff --git a/src/tests/kits/net/ppp/src/KPPPInterface.cpp b/src/tests/kits/net/ppp/src/KPPPInterface.cpp index 94c5203661..0b0d739274 100644 --- a/src/tests/kits/net/ppp/src/KPPPInterface.cpp +++ b/src/tests/kits/net/ppp/src/KPPPInterface.cpp @@ -26,7 +26,7 @@ PPPInterface::PPPInterface(driver_settings *settings, PPPInterface *parent = NULL) : fSettings(dup_driver_settings(settings)), fStateMachine(*this), fLCP(*this), fReportManager(StateMachine().Locker()), - fIfnet(NULL), fLinkMTU(1500), fAccessing(0), fChildrenCount(0), + fIfnet(NULL), fUpThread(-1), fLinkMTU(1500), fAccessing(0), fChildrenCount(0), fDevice(NULL), fFirstEncapsulator(NULL), fLock(StateMachine().Locker()) { if(get_module(PPP_MANAGER_MODULE_NAME, (module_info**) &fManager) != B_OK) @@ -344,6 +344,9 @@ PPPInterface::EncapsulatorFor(uint16 protocol, bool PPPInterface::AddChild(PPPInterface *child) { + if(!child || child->Mode() != Mode()) + return false; + LockerHelper locker(fLock); if(fChildren.HasItem(child) || !fChildren.AddItem(child)) @@ -402,28 +405,88 @@ PPPInterface::SetDialOnDemand(bool dialondemand = true) // check if we need to register/unregister if(!Ifnet() && fDialOnDemand) RegisterInterface(); - else if(Ifnet() && !fDialOnDemand && Phase() == PPP_DOWN_PHASE) + else if(Ifnet() && !fDialOnDemand && Phase() == PPP_DOWN_PHASE) { UnregisterInterface(); + Delete(); + } } bool PPPInterface::Up() { - // ToDo: - // instead of waiting for state change we should wait until - // all retries are done - - if(!InitCheck() || Phase() != PPP_DOWN_PHASE) + if(!InitCheck() || Phase() == PPP_TERMINATION_PHASE) return false; if(IsUp()) return true; - // TODO: - // Add one-time connection report request and wait - // for results. If we lost the connection we should - // consider redialing. + ppp_report_packet report; + thread_id me = find_thread(NULL), sender; + + // one thread has to do the real task while all other threads are observers + fLock.Lock(); + if(fUpThread == -1) + fUpThread = me; + + fReportManager.EnableReports(PPP_CONNECTION_REPORT, me, PPP_WAIT_FOR_REPLY); + fLock.Unlock(); + + if(me != fUpThread) { + // I am an observer + while(true) { + if(IsUp()) { + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } + + if(receive_data(&sender, &report, sizeof(report)) != PPP_REPORT_CODE) { + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } + + if(IsUp()) { + PPP_REPLY(sender, B_OK); + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } + + if(report.type != PPP_CONNECTION_REPORT) { + PPP_REPLY(sender, B_OK); + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + } + + if(report.code == PPP_GOING_UP) { + PPP_REPLY(sender, B_OK); + continue; + } else if(report.code == PPP_UP_SUCCESSFUL) { + PPP_REPLY(sender, B_OK); + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } else if(report.code == PPP_DOWN_SUCCESSFUL + || report.code == PPP_UP_ABORTED) { + PPP_REPLY(sender, B_OK); + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } else if(report.code == PPP_UP_FAILED) { + // TODO: + // if maximum number of retries is reached we return false + // otherwise we wait for the next dial-attempt + } else if(report.code == PPP_AUTHENTICATION_FAILED) { + PPP_REPLY(sender, B_OK); + fReportManager.DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } else if(report.code == PPP_CONNECTION_LOST) { + // TODO: + // if autoredial is enabled wait for redial attemts (just continue) + } + } + } else { + // I am the thread for the real task + while(true) { + + } + } return false; } diff --git a/src/tests/kits/net/ppp/src/KPPPReportManager.cpp b/src/tests/kits/net/ppp/src/KPPPReportManager.cpp index 0ccb69d278..b44e760cb9 100644 --- a/src/tests/kits/net/ppp/src/KPPPReportManager.cpp +++ b/src/tests/kits/net/ppp/src/KPPPReportManager.cpp @@ -82,7 +82,7 @@ PPPReportManager::Report(PPP_REPORT_TYPE type, int32 code, void *data, int32 len for(int32 index = 0; index < fReportRequests.CountItems(); index++) { ppp_report_request& request = fReportRequests.ItemAt(index); - result = send_data_with_timeout(request.port, PPP_REPORT_CODE, &report, + result = send_data_with_timeout(request.thread, PPP_REPORT_CODE, &report, sizeof(report), PPP_REPORT_TIMEOUT); if(result == B_BAD_THREAD_ID || result == B_NO_MEMORY) { @@ -91,10 +91,21 @@ PPPReportManager::Report(PPP_REPORT_TYPE type, int32 code, void *data, int32 len continue; } else if(result == B_OK) { if(request.flags & PPP_WAIT_FOR_REPLY) { - result = receive_data_with_timeout(fPort, &code, NULL, 0, - PPP_REPORT_TIMEOUT); + if(request.flags & PPP_NO_REPLY_TIMEOUT) { + sender = -1; + while(sender != request.thread) + code = receive_data(&sender, NULL, 0); + result = B_OK; + } else { + sender = -1; + result = B_OK; + while(sender != request.thread && result == B_OK) + result = receive_data_with_timeout(&sender, &code, NULL, 0, + PPP_REPORT_TIMEOUT); + } + if(result == B_OK && code != B_OK) - successful = false; + acceptable = false; } } diff --git a/src/tests/kits/net/ppp/src/KPPPStateMachine.cpp b/src/tests/kits/net/ppp/src/KPPPStateMachine.cpp index cecea663b7..f0319697dd 100644 --- a/src/tests/kits/net/ppp/src/KPPPStateMachine.cpp +++ b/src/tests/kits/net/ppp/src/KPPPStateMachine.cpp @@ -2,7 +2,11 @@ // TODO: -// do not forget to reset authentication status when needed +// do not forget to reset authentication status when: +// - connection is lost +// - reconfiguring? +// - terminating +// - ... PPPStateMachine::PPPStateMachine(PPPInterface& interface) : fInterface(&interface), fPhase(PPP_DOWN_PHASE), @@ -49,8 +53,16 @@ PPPStateMachine::NewPhase(PPP_PHASE next) { // Report a down event to parent if we are not usable anymore. // The report threads get their notification later. - if(Phase() == PPP_ESTABLISHED_PHASE && Interface()->Parent()) - Interface()->Parent()->StateMachine().DownEvent(Interface()); + if(Phase() == PPP_ESTABLISHED_PHASE && next != Phase()) { + if(Interface()->Ifnet()) + Interface()->Ifnet()->if_flags &= ~IFF_RUNNING; + + if(!Interface()->DoesDialOnDemand()) + Interface()->UnregisterInterface(); + + if(Interface()->Parent()) + Interface()->Parent()->StateMachine().DownEvent(Interface()); + } // there is nothing after established phase and nothing before down phase if(next > PPP_ESTABLISHED_PHASE) @@ -161,13 +173,14 @@ PPPStateMachine::UpEvent(PPPInterface *interface) LockerHelper locker(fLock); if(Phase() <= PPP_TERMINATION_PHASE || State() != PPP_STARTING_STATE) { - interface->Down(); + interface->StateMachine().CloseEvent(); return; } NewState(PPP_OPENED_STATE); if(Phase() == PPP_ESTABLISHMENT_PHASE) { NewPhase(PPP_AUTHENTICATION_PHASE); + locker.UnlockNow(); ThisLayerUp(); } } @@ -189,8 +202,10 @@ PPPStateMachine::DownEvent(PPPInterface *interface) ++count; } - if(count == 0 && Interface()->Ifnet()) - Interface()->Ifnet()->if_flags &= ~IFF_RUNNING; + if(count == 0) { + locker.UnlockNow(); + DownEvent(); + } } } @@ -198,8 +213,8 @@ PPPStateMachine::DownEvent(PPPInterface *interface) void PPPStateMachine::UpFailedEvent(PPPProtocol *protocol) { - // All protocols must go up. - CloseEvent(); + if(protocol->Flags() & PPP_NOT_IMPORTANT) + CloseEvent(); } @@ -222,8 +237,8 @@ PPPStateMachine::DownEvent(PPPProtocol *protocol) void PPPStateMachine::UpFailedEvent(PPPEncapsulator *encapsulator) { - // All encapsulators must go up. - CloseEvent(); + if(encapsulator->Flags() & PPP_NOT_IMPORTANT) + CloseEvent(); } @@ -337,9 +352,15 @@ PPPStateMachine::UpEvent() NewState(PPP_CLOSED_STATE); locker.UnlockNow(); ThisLayerFinished(); + + return; } // TODO: handle server-up! + NewState(PPP_REQ_SENT_STATE); + InitializeRestartCount(); + locker.UnlockNow(); + SendConfigureRequest(); break; case PPP_STARTING_STATE: @@ -381,7 +402,7 @@ PPPStateMachine::DownEvent() 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); + NewState(PPP_STARTING_STATE); break; case PPP_STOPPING_STATE: @@ -389,7 +410,7 @@ PPPStateMachine::DownEvent() case PPP_ACK_RCVD_STATE: case PPP_ACK_SENT_STATE: case PPP_OPENED_STATE: - NewStatus(PPP_STARTING_STATE); + NewState(PPP_STARTING_STATE); break; default: @@ -406,7 +427,9 @@ PPPStateMachine::DownEvent() // maybe we need to redial if(State() == PPP_STARTING_STATE) { if(fAuthentiactionStatus == PPP_AUTHENTICATION_FAILED - || fPeerAuthenticationStatus == PPP_AUTHENTICATION_FAILED) + || fAuthenticationStatus == PPP_AUTHENTICATING + || fPeerAuthenticationStatus == PPP_AUTHENTICATION_FAILED + || fPeerAuthenticationStatus == PPP_AUTHENTICATING) Interface()->Report(PPP_CONNECTION_REPORT, PPP_AUTHENTICATION_FAILED, NULL, 0); else @@ -421,10 +444,14 @@ PPPStateMachine::DownEvent() if(Interface()->DoesAutoRedial()) { // TODO: // Redial() - } else + } else if(!Interface()->DoesDialOnDemand()) Interface()->Delete(); - } else - Report(PPP_CONNECTION_REPORT, PPP_DOWN_SUCCESSFUL, NULL, 0); + } else { + Interface()->Report(PPP_CONNECTION_REPORT, PPP_DOWN_SUCCESSFUL, NULL, 0); + + if(!Interface()->DoesDialOnDemand()) + Interface()->Delete(); + } } @@ -436,11 +463,21 @@ PPPStateMachine::OpenEvent() switch(State()) { case PPP_INITIAL_STATE: - NewState(PPP_STARTING_STATE); + if(!Interface()->Report(PPP_CONNECTION_REPORT, PPP_GOING_UP, NULL, 0)) + return; + + if(Interface()->Mode() == PPP_SERVER_MODE) { + NewPhase(PPP_ESTABLISHMENT_PHASE); + + if(Interface()->Device()) + Interface()->Device()->Listen(); + } else + NewState(PPP_STARTING_STATE); + if(Interface()->IsMultilink() && !Interface()->Parent()) { NewPhase(PPP_ESTABLISHMENT_PHASE); for(int32 i = 0; i < Interface()->CountChildren(); i++) - Interface()->ChildAt(i)->Up(); + Interface()->ChildAt(i)->StateMachine().OpenEvent(); } else { locker.UnlockNow(); ThisLayerStarted(); @@ -473,12 +510,14 @@ PPPStateMachine::CloseEvent() if(Interface()->IsMultilink() && !Interface()->Parent()) { NewState(PPP_INITIAL_STATE); - NewPhase(PPP_DOWN_PHASE); + + if(Phase() != PPP_DOWN_PHASE) + NewPhase(PPP_TERMINATION_PHASE); ThisLayerDown(); for(int32 i = 0; i < Interface()->CountChildren(); i++) - Interface()->ChildAt(i)->Down(); + Interface()->ChildAt(i)->StateMachine().CloseEvent(); return; } @@ -1328,7 +1367,7 @@ PPPStateMachine::BringHandlersUp() if(BringPhaseUp() > 0) break; - fLock.Lock(); + LockerHelper locker(fLock); if(Phase() < PPP_AUTHENTICATION_PHASE) return; @@ -1336,14 +1375,12 @@ PPPStateMachine::BringHandlersUp() if(Interface()->Parent()) Interface()->Parent()->StateMachine().UpEvent(Interface()); - // TODO: - // Report(PPP_CONNECTION_REPORT, PPP_UP_SUCCESSFUL, NULL, 0); if(Interface()->Ifnet()) Interface()->Ifnet()->if_flags |= IFF_RUNNING; + + Interface()->Report(PPP_CONNECTION_REPORT, PPP_UP_SUCCESSFUL, NULL, 0); } else NewPhase(Phase() + 1); - - fLock.Unlock(); } }