diff --git a/src/add-ons/kernel/network/Jamfile b/src/add-ons/kernel/network/Jamfile index fa6c85a771..a272c692d2 100644 --- a/src/add-ons/kernel/network/Jamfile +++ b/src/add-ons/kernel/network/Jamfile @@ -3,4 +3,4 @@ SubDir OBOS_TOP src add-ons kernel network ; SubInclude OBOS_TOP src add-ons kernel network core ; SubInclude OBOS_TOP src add-ons kernel network interfaces ; SubInclude OBOS_TOP src add-ons kernel network protocols ; - +SubInclude OBOS_TOP src add-ons kernel network ppp ; diff --git a/src/add-ons/kernel/network/interfaces/Jamfile b/src/add-ons/kernel/network/interfaces/Jamfile index 39e28b9c09..94b52624c7 100644 --- a/src/add-ons/kernel/network/interfaces/Jamfile +++ b/src/add-ons/kernel/network/interfaces/Jamfile @@ -2,4 +2,4 @@ SubDir OBOS_TOP src add-ons kernel network interfaces ; SubInclude OBOS_TOP src add-ons kernel network interfaces ethernet ; SubInclude OBOS_TOP src add-ons kernel network interfaces loopback ; - +SubInclude OBOS_TOP src add-ons kernel network interfaces ppp ; diff --git a/src/add-ons/kernel/network/ppp/Jamfile b/src/add-ons/kernel/network/ppp/Jamfile new file mode 100644 index 0000000000..63823aeaaf --- /dev/null +++ b/src/add-ons/kernel/network/ppp/Jamfile @@ -0,0 +1,3 @@ +SubDir OBOS_TOP src add-ons kernel network ppp ; + +SubInclude OBOS_TOP src add-ons kernel network ppp shared ; diff --git a/src/add-ons/kernel/network/ppp/shared/Jamfile b/src/add-ons/kernel/network/ppp/shared/Jamfile new file mode 100644 index 0000000000..f4c597f297 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/Jamfile @@ -0,0 +1,3 @@ +SubDir OBOS_TOP src add-ons kernel network ppp shared ; + +SubInclude OBOS_TOP src add-ons kernel network ppp shared libkernelppp ; diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/Jamfile b/src/add-ons/kernel/network/ppp/shared/libkernelppp/Jamfile new file mode 100644 index 0000000000..d392d6f84b --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/Jamfile @@ -0,0 +1,31 @@ +SubDir OBOS_TOP src add-ons kernel network ppp shared libkernelppp ; + +# for cpp.cpp and BLocker +SEARCH_SOURCE += [ FDirName $(OBOS_TOP) src add-ons kernel file_systems bfs ] ; +SEARCH_SOURCE += [ FDirName $(OBOS_TOP) src kernel core disk_device_manager ] ; +UseHeaders [ FDirName $(OBOS_TOP) src add-ons kernel file_systems bfs ] ; + +UsePrivateHeaders net ; +UseHeaders [ FDirName $(OBOS_TOP) src add-ons kernel network ppp shared libkernelppp headers ] ; + +R5KernelStaticLibrary kernelppp : + KPPPConfigurePacket.cpp + KPPPDevice.cpp + KPPPEncapsulator.cpp + KPPPInterface.cpp + KPPPLCP.cpp + KPPPLCPExtension.cpp + KPPPOptionHandler.cpp + KPPPProtocol.cpp + KPPPReportManager.cpp + KPPPStateMachine.cpp + KPPPUtils.cpp + Locker.cpp + settings_tools.cpp + cpp.cpp + + # integrated modules + _KPPPMRUHandler.cpp + _KPPPAuthenticationHandler.cpp + _KPPPPFCHandler.cpp +; diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPConfigurePacket.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPConfigurePacket.cpp new file mode 100644 index 0000000000..62475750f7 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPConfigurePacket.cpp @@ -0,0 +1,154 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include + + +PPPConfigurePacket::PPPConfigurePacket(uint8 code) + : fCode(code) +{ +} + + +PPPConfigurePacket::PPPConfigurePacket(struct mbuf *packet) +{ + // decode packet + ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*); + + if(!SetCode(data->code)) + return; + + if(data->length < 4) + return; + // there are no items (or one corrupted item) + + int32 position = 0; + ppp_configure_item *item; + + while(position <= data->length - 4) { + item = (ppp_configure_item*) (data->data + position); + position += item->length; + + AddItem(item); + } +} + + +PPPConfigurePacket::~PPPConfigurePacket() +{ + for(int32 index = 0; index < CountItems(); index++) + free(ItemAt(index)); +} + + +bool +PPPConfigurePacket::SetCode(uint8 code) +{ + // only configure codes are allowed! + if(code < PPP_CONFIGURE_REQUEST || code > PPP_CONFIGURE_REJECT) + return false; + + fCode = code; + + return true; +} + + +bool +PPPConfigurePacket::AddItem(const ppp_configure_item *item, int32 index = -1) +{ + if(item->length < 2) + return false; + + ppp_configure_item *add = (ppp_configure_item*) malloc(item->length); + memcpy(add, item, item->length); + + bool status; + if(index < 0) + status = fItems.AddItem(add); + else + status = fItems.AddItem(add, index); + if(!status) { + free(add); + return false; + } + + return true; +} + + +bool +PPPConfigurePacket::RemoveItem(ppp_configure_item *item) +{ + if(!fItems.HasItem(item)) + return false; + + fItems.RemoveItem(item); + free(item); + + return true; +} + + +ppp_configure_item* +PPPConfigurePacket::ItemAt(int32 index) const +{ + ppp_configure_item *item = fItems.ItemAt(index); + + if(item == fItems.GetDefaultItem()) + return NULL; + + return item; +} + + +ppp_configure_item* +PPPConfigurePacket::ItemWithType(uint8 type) const +{ + ppp_configure_item *item; + + for(int32 index = 0; index < CountItems(); index++) { + item = ItemAt(index); + if(item && item->type == type) + return item; + } + + return NULL; +} + + +struct mbuf* +PPPConfigurePacket::ToMbuf(uint32 reserve = 0) +{ + struct mbuf *packet = m_gethdr(MT_DATA); + packet->m_data += reserve; + + ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*); + + data->code = Code(); + + uint8 length = 0; + ppp_configure_item *item; + + for(int32 index = 0; index < CountItems(); index++) { + item = ItemAt(index); + + if(0xFF - length < item->length) { + m_freem(packet); + return NULL; + } + + memcpy(data->data + length, item, item->length); + length += item->length; + } + + data->length = length + 2; + packet->m_len = data->length; + + return packet; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPDevice.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPDevice.cpp new file mode 100644 index 0000000000..c2a0de388e --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPDevice.cpp @@ -0,0 +1,130 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include + +#include +#include + +#include + + +PPPDevice::PPPDevice(const char *name, PPPInterface& interface, + driver_parameter *settings) + : fMTU(1500), + fIsUp(false), + fInterface(interface), + fSettings(settings) +{ + if(name) + fName = strdup(name); + else + fName = strdup("Unknown"); + + fInitStatus = interface.SetDevice(this) ? B_OK : B_ERROR; +} + + +PPPDevice::~PPPDevice() +{ + free(fName); + + Interface().SetDevice(NULL); +} + + +status_t +PPPDevice::InitCheck() const +{ + return fInitStatus; +} + + +status_t +PPPDevice::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_DEVICE_INFO: { + if(length < sizeof(ppp_device_info_t) || !data) + return B_NO_MEMORY; + + ppp_device_info *info = (ppp_device_info*) data; + memset(info, 0, sizeof(ppp_device_info_t)); + strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); + info->settings = Settings(); + info->MTU = MTU(); + info->inputTransferRate = InputTransferRate(); + info->outputTransferRate = OutputTransferRate(); + info->outputBytesCount = CountOutputBytes(); + } break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPDevice::PassToInterface(struct mbuf *packet) +{ + if(!Interface().InQueue()) + return B_ERROR; + + IFQ_ENQUEUE(Interface().InQueue(), packet); + + return B_OK; +} + + +void +PPPDevice::Pulse() +{ + // do nothing by default +} + + +bool +PPPDevice::UpStarted() const +{ + return Interface().StateMachine().TLSNotify(); +} + + +bool +PPPDevice::DownStarted() const +{ + return Interface().StateMachine().TLFNotify(); +} + + +void +PPPDevice::UpFailedEvent() +{ + fIsUp = false; + + Interface().StateMachine().UpFailedEvent(); +} + + +void +PPPDevice::UpEvent() +{ + fIsUp = true; + + Interface().StateMachine().UpEvent(); +} + + +void +PPPDevice::DownEvent() +{ + fIsUp = false; + + Interface().StateMachine().DownEvent(); +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPEncapsulator.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPEncapsulator.cpp new file mode 100644 index 0000000000..540129e9a9 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPEncapsulator.cpp @@ -0,0 +1,191 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include +#include "settings_tools.h" + +#include + + +PPPEncapsulator::PPPEncapsulator(const char *name, ppp_phase phase, + ppp_encapsulation_level level, uint16 protocol, + int32 addressFamily, uint32 overhead, + PPPInterface& interface, driver_parameter *settings, + int32 flags = PPP_NO_FLAGS) + : fOverhead(overhead), + fPhase(phase), + fLevel(level), + fProtocol(protocol), + fAddressFamily(addressFamily), + fInterface(interface), + fSettings(settings), + fFlags(flags), + fEnabled(true), + fUpRequested(true), + fConnectionStatus(PPP_DOWN_PHASE) +{ + if(name) + fName = strdup(name); + else + fName = strdup("Unknown"); + + const char *sideString = get_parameter_value("side", settings); + if(sideString) + fSide = get_side_string_value(sideString, PPP_LOCAL_SIDE); + else { + if(interface.Mode() == PPP_CLIENT_MODE) + fSide = PPP_LOCAL_SIDE; + else + fSide = PPP_PEER_SIDE; + } + + fInitStatus = interface.AddEncapsulator(this) ? B_OK : B_ERROR; +} + + +PPPEncapsulator::~PPPEncapsulator() +{ + free(fName); + + Interface().RemoveEncapsulator(this); +} + + +status_t +PPPEncapsulator::InitCheck() const +{ + return fInitStatus; +} + + +void +PPPEncapsulator::SetEnabled(bool enabled = true) +{ + fEnabled = enabled; + + if(!enabled) { + if(IsUp() || IsGoingUp()) + Down(); + } else if(!IsUp() && !IsGoingUp() && IsUpRequested() && Interface().IsUp()) + Up(); +} + + +status_t +PPPEncapsulator::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_HANDLER_INFO: { + if(length < sizeof(ppp_handler_info_t) || !data) + return B_ERROR; + + ppp_handler_info *info = (ppp_handler_info*) data; + memset(info, 0, sizeof(ppp_handler_info_t)); + strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); + info->settings = Settings(); + info->phase = Phase(); + info->addressFamily = AddressFamily(); + info->flags = Flags(); + info->side = Side(); + info->protocol = Protocol(); + info->isEnabled = IsEnabled(); + info->isUpRequested = IsUpRequested(); + info->connectionStatus = fConnectionStatus; + info->level = Level(); + info->overhead = Overhead(); + } break; + + case PPPC_SET_ENABLED: + if(length < sizeof(uint32) || !data) + return B_ERROR; + + SetEnabled(*((uint32*)data)); + break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPEncapsulator::StackControl(uint32 op, void *data) +{ + switch(op) { + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPEncapsulator::SendToNext(struct mbuf *packet, uint16 protocol) const +{ + // Find the next possible handler for this packet. + // This handler should be enabled, up, and allowed to send + if(Next()) { + if(Next()->IsEnabled() && Next()->IsUp() + && is_handler_allowed(*Next(), Interface().State(), Interface().Phase())) + return Next()->Send(packet, protocol); + else + return Next()->SendToNext(packet, protocol); + } else + return Interface().SendToDevice(packet, protocol); +} + + +void +PPPEncapsulator::Pulse() +{ + // do nothing by default +} + + +void +PPPEncapsulator::UpStarted() +{ + fConnectionStatus = PPP_ESTABLISHMENT_PHASE; +} + + +void +PPPEncapsulator::DownStarted() +{ + fConnectionStatus = PPP_TERMINATION_PHASE; +} + + +void +PPPEncapsulator::UpFailedEvent() +{ + fConnectionStatus = PPP_DOWN_PHASE; + + Interface().StateMachine().UpFailedEvent(this); +} + + +void +PPPEncapsulator::UpEvent() +{ + fConnectionStatus = PPP_ESTABLISHED_PHASE; + + Interface().StateMachine().UpEvent(this); +} + + +void +PPPEncapsulator::DownEvent() +{ + fConnectionStatus = PPP_DOWN_PHASE; + + Interface().StateMachine().DownEvent(this); +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPInterface.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPInterface.cpp new file mode 100644 index 0000000000..e6b212b5f3 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPInterface.cpp @@ -0,0 +1,1650 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +// stdio.h must be included before PPPModule.h/PPPManager.h because +// dprintf is defined twice with different return values, once with +// void (KernelExport.h) and once with int (stdio.h). +#include +#include +#include + +// now our headers... +#include + +// our other classes +#include +#include +#include +#include +#include +#include +#include +#include + +// general helper classes not only belonging to us +#include + +// tools only for us :) +#include "settings_tools.h" + +// internal modules +#include "_KPPPMRUHandler.h" +#include "_KPPPAuthenticationHandler.h" +#include "_KPPPPFCHandler.h" + + +// TODO: +// - implement timers with support for settings next time instead of receiving timer +// events periodically + + +// needed for redial: +typedef struct redial_info { + PPPInterface *interface; + thread_id *thread; + uint32 delay; +} redial_info; + +status_t redial_thread(void *data); + +// other functions +status_t in_queue_thread(void *data); +status_t interface_deleter_thread(void *data); + + +PPPInterface::PPPInterface(uint32 ID, driver_settings *settings, + PPPInterface *parent = NULL) + : fID(ID), + fSettings(dup_driver_settings(settings)), + fStateMachine(*this), + fLCP(*this), + fReportManager(StateMachine().Locker()), + fIfnet(NULL), + fUpThread(-1), + fRedialThread(-1), + fDialRetry(0), + fDialRetriesLimit(0), + fIdleSince(0), + fMRU(1500), + fInterfaceMTU(1500), + fHeaderLength(2), + fAutoRedial(false), + fDialOnDemand(false), + fLocalPFCState(PPP_PFC_DISABLED), + fPeerPFCState(PPP_PFC_DISABLED), + fPFCOptions(0), + fDevice(NULL), + fFirstEncapsulator(NULL), + fLock(StateMachine().Locker()), + fDeleteCounter(0) +{ + // add internal modules + // MRU + _PPPMRUHandler *mruHandler = + new _PPPMRUHandler(*this); + if(mruHandler->InitCheck() != B_OK) + delete mruHandler; + // authentication + _PPPAuthenticationHandler *authenticationHandler = + new _PPPAuthenticationHandler(*this); + if(authenticationHandler->InitCheck() != B_OK) + delete authenticationHandler; + // PFC + _PPPPFCHandler *pfcHandler = + new _PPPPFCHandler(fLocalPFCState, fPeerPFCState, *this); + if(pfcHandler->InitCheck() != B_OK) + delete pfcHandler; + + // set up dial delays + fDialRetryDelay = 3000; + // 3s delay between each new attempt to redial + fRedialDelay = 1000; + // 1s delay between lost connection and redial + + // set up queue + fInQueue = start_ifq(); + fInQueueThread = spawn_thread(in_queue_thread, "PPPInterface: in_queue_thread", + B_NORMAL_PRIORITY, this); + resume_thread(fInQueueThread); + + if(get_module(PPP_MANAGER_MODULE_NAME, (module_info**) &fManager) != B_OK) + fManager = NULL; + + // are we a multilink subinterface? + if(parent && parent->IsMultilink()) { + fParent = parent; + fParent->AddChild(this); + fIsMultilink = true; + } else { + fParent = NULL; + fIsMultilink = false; + } + + if(!fSettings) { + fMode = PPP_CLIENT_MODE; + fInitStatus = B_ERROR; + return; + } + + const char *value; + + // get DisonnectAfterIdleSince settings + value = get_settings_value(PPP_DISONNECT_AFTER_IDLE_SINCE_KEY, fSettings); + if(!value) + fDisconnectAfterIdleSince = 0; + else + fDisconnectAfterIdleSince = atoi(value) * 1000; + + if(fDisconnectAfterIdleSince < 0) + fDisconnectAfterIdleSince = 0; + + // get mode settings + value = get_settings_value(PPP_MODE_KEY, fSettings); + if(!strcasecmp(value, PPP_SERVER_MODE_VALUE)) + fMode = PPP_SERVER_MODE; + else + fMode = PPP_CLIENT_MODE; + // we are a client by default + + SetDialOnDemand( + get_boolean_value( + get_settings_value(PPP_DIAL_ON_DEMAND_KEY, fSettings), + false) + ); + // dial on demand is disabled by default + + + SetAutoRedial( + get_boolean_value( + get_settings_value(PPP_AUTO_REDIAL_KEY, fSettings), + false) + ); + // auto redial is disabled by default + + // load all protocols and the device + if(LoadModules(fSettings, 0, fSettings->parameter_count)) + fInitStatus = B_OK; + else + fInitStatus = B_ERROR; +} + + +PPPInterface::~PPPInterface() +{ + ++fDeleteCounter; + + // make sure we are not accessible by any thread before we continue + UnregisterInterface(); + + if(fManager) + fManager->remove_interface(ID()); + + // Call Down() until we get a lock on an interface that is down. + // This lock is not released until we are actually deleted. + while(true) { + Down(); + fLock.Lock(); + if(State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) + break; + fLock.Unlock(); + } + + Report(PPP_DESTRUCTION_REPORT, 0, NULL, 0); + // tell all listeners that we are being destroyed + + int32 tmp; + stop_ifq(InQueue()); + wait_for_thread(fInQueueThread, &tmp); + + send_data_with_timeout(fRedialThread, 0, NULL, 0, 200); + // tell thread that we are being destroyed (200ms timeout) + wait_for_thread(fRedialThread, &tmp); + + while(CountChildren()) + delete ChildAt(0); + + delete Device(); + + while(CountProtocols()) + delete ProtocolAt(0); + + while(FirstEncapsulator()) + delete FirstEncapsulator(); + + for(int32 index = 0; index < fModules.CountItems(); index++) { + put_module(fModules.ItemAt(index)); + free(fModules.ItemAt(index)); + } + + free_driver_settings(fSettings); + + if(Parent()) + Parent()->RemoveChild(this); + + if(fManager) + put_module(PPP_MANAGER_MODULE_NAME); +} + + +void +PPPInterface::Delete() +{ + if(atomic_add(&fDeleteCounter, 1) > 0) + return; + // only one thread should delete us! + + if(fManager) + fManager->delete_interface(ID()); + // This will mark us for deletion. + // Any subsequent calls to delete_interface() will do nothing. + else { + // We were not created by the manager. + // Spawn a thread that will delete us. + thread_id interfaceDeleterThread = spawn_thread(interface_deleter_thread, + "PPPInterface: interface_deleter_thread", B_NORMAL_PRIORITY, this); + resume_thread(interfaceDeleterThread); + } +} + + +status_t +PPPInterface::InitCheck() const +{ + if(!fSettings || !fManager || fInitStatus != B_OK) + 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; +} + + +bool +PPPInterface::SetMRU(uint32 MRU) +{ + if(MRU > Device()->MTU() - 2) + return false; + + LockerHelper locker(fLock); + + fMRU = MRU; + + CalculateInterfaceMTU(); + + return true; +} + + +status_t +PPPInterface::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_INTERFACE_INFO: { + if(length < sizeof(ppp_interface_info_t) || !data) + return B_NO_MEMORY; + + ppp_interface_info *info = (ppp_interface_info*) data; + memset(info, 0, sizeof(ppp_interface_info_t)); + info->settings = Settings(); + info->mode = Mode(); + info->state = State(); + info->phase = Phase(); + info->localAuthenticationStatus = + StateMachine().LocalAuthenticationStatus(); + info->peerAuthenticationStatus = + StateMachine().PeerAuthenticationStatus(); + info->localPFCState = LocalPFCState(); + info->peerPFCState = PeerPFCState(); + info->pfcOptions = PFCOptions(); + info->protocolsCount = CountProtocols(); + info->encapsulatorsCount = CountEncapsulators(); + info->optionHandlersCount = LCP().CountOptionHandlers(); + info->LCPExtensionsCount = 0; + info->childrenCount = CountChildren(); + info->MRU = MRU(); + info->interfaceMTU = InterfaceMTU(); + info->dialRetry = fDialRetry; + info->dialRetriesLimit = fDialRetriesLimit; + info->dialRetryDelay = DialRetryDelay(); + info->redialDelay = RedialDelay(); + info->idleSince = IdleSince(); + info->disconnectAfterIdleSince = DisconnectAfterIdleSince(); + info->doesDialOnDemand = DoesDialOnDemand(); + info->doesAutoRedial = DoesAutoRedial(); + info->hasDevice = Device(); + info->isMultilink = IsMultilink(); + info->hasParent = Parent(); + } break; + + case PPPC_SET_MRU: + if(length < sizeof(uint32) || !data) + return B_ERROR; + + SetMRU(*((uint32*)data)); + break; + + case PPPC_SET_DIAL_ON_DEMAND: + if(length < sizeof(uint32) || !data) + return B_NO_MEMORY; + + SetDialOnDemand(*((uint32*)data)); + break; + + case PPPC_SET_AUTO_REDIAL: + if(length < sizeof(uint32) || !data) + return B_NO_MEMORY; + + SetAutoRedial(*((uint32*)data)); + break; + + case PPPC_ENABLE_INTERFACE_REPORTS: { + if(length < sizeof(ppp_report_request) || !data) + return B_ERROR; + + ppp_report_request *request = (ppp_report_request*) data; + ReportManager().EnableReports(request->type, request->thread, + request->flags); + } break; + + case PPPC_DISABLE_INTERFACE_REPORTS: { + if(length < sizeof(ppp_report_request) || !data) + return B_ERROR; + + ppp_report_request *request = (ppp_report_request*) data; + ReportManager().DisableReports(request->type, request->thread); + } break; + + case PPPC_CONTROL_DEVICE: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + if(control->index != 0 || !Device()) + return B_BAD_INDEX; + + return Device()->Control(control->op, control->data, control->length); + } break; + + case PPPC_CONTROL_PROTOCOL: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + PPPProtocol *protocol_handler = ProtocolAt(control->index); + if(!protocol_handler) + return B_BAD_INDEX; + + return protocol_handler->Control(control->op, control->data, + control->length); + } break; + + case PPPC_CONTROL_ENCAPSULATOR: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + PPPEncapsulator *encapsulator_handler = EncapsulatorAt(control->index); + if(!encapsulator_handler) + return B_BAD_INDEX; + + return encapsulator_handler->Control(control->op, control->data, + control->length); + } break; + + case PPPC_CONTROL_OPTION_HANDLER: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + PPPOptionHandler *option_handler = LCP().OptionHandlerAt(control->index); + if(!option_handler) + return B_BAD_INDEX; + + return option_handler->Control(control->op, control->data, + control->length); + } break; + + case PPPC_CONTROL_LCP_EXTENSION: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + PPPLCPExtension *extension_handler = LCP().LCPExtensionAt(control->index); + if(!extension_handler) + return B_BAD_INDEX; + + return extension_handler->Control(control->op, control->data, + control->length); + } break; + + case PPPC_CONTROL_CHILD: { + if(length < sizeof(ppp_control_info) || !data) + return B_ERROR; + + ppp_control_info *control = (ppp_control_info*) data; + PPPInterface *child = ChildAt(control->index); + if(!child) + return B_BAD_INDEX; + + return child->Control(control->op, control->data, control->length); + } break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +bool +PPPInterface::SetDevice(PPPDevice *device) +{ + if(!device) + return false; + + 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 + + if(fDevice && (IsUp() || fDevice->IsUp())) + Down(); + + fDevice = device; + + fMRU = fDevice->MTU() - 2; + + CalculateInterfaceMTU(); + CalculateBaudRate(); + + return true; +} + + +bool +PPPInterface::AddProtocol(PPPProtocol *protocol) +{ + if(!protocol) + return false; + + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change and there may only be + // one authenticator protocol for each protocol number + + fProtocols.AddItem(protocol); + + if(IsUp() || Phase() >= protocol->Phase()) + protocol->Up(); + + return true; +} + + +bool +PPPInterface::RemoveProtocol(PPPProtocol *protocol) +{ + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + if(!fProtocols.HasItem(protocol)) + return false; + + if(IsUp() || Phase() >= protocol->Phase()) + protocol->Down(); + + return fProtocols.RemoveItem(protocol); +} + + +PPPProtocol* +PPPInterface::ProtocolAt(int32 index) const +{ + PPPProtocol *protocol = fProtocols.ItemAt(index); + + if(protocol == fProtocols.GetDefaultItem()) + return NULL; + + return protocol; +} + + +PPPProtocol* +PPPInterface::ProtocolFor(uint16 protocol, int32 *start = NULL) const +{ + // The iteration style in this method is strange C/C++. + // Explanation: I use this style because it makes extending all XXXFor + // methods simpler as that they look very similar, now. + + int32 index = start ? *start : 0; + + if(index < 0) + return NULL; + + PPPProtocol *current = ProtocolAt(index); + + for(; current; current = ProtocolAt(++index)) { + if(current->Protocol() == protocol + || (current->Flags() & PPP_INCLUDES_NCP + && current->Protocol() & 0x7FFF == protocol & 0x7FFF)) { + if(start) + *start = index; + return current; + } + } + + return NULL; +} + + +bool +PPPInterface::AddEncapsulator(PPPEncapsulator *encapsulator) +{ + // Find instert position after the last encapsulator + // with the same level. + + if(!encapsulator) + return false; + + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + PPPEncapsulator *current = fFirstEncapsulator, *previous = NULL; + + while(current) { + if(current->Level() < encapsulator->Level()) + break; + + previous = current; + current = current->Next(); + } + + if(!current) { + if(!previous) + fFirstEncapsulator = encapsulator; + else + previous->SetNext(encapsulator); + + encapsulator->SetNext(NULL); + } else { + encapsulator->SetNext(current); + + if(!previous) + fFirstEncapsulator = encapsulator; + else + previous->SetNext(encapsulator); + } + + CalculateInterfaceMTU(); + + if(IsUp()) + encapsulator->Up(); + + return true; +} + + +bool +PPPInterface::RemoveEncapsulator(PPPEncapsulator *encapsulator) +{ + LockerHelper locker(fLock); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + PPPEncapsulator *current = fFirstEncapsulator, *previous = NULL; + + while(current) { + if(current == encapsulator) { + if(IsUp()) + encapsulator->Down(); + + if(previous) + previous->SetNext(current->Next()); + else + fFirstEncapsulator = current->Next(); + + current->SetNext(NULL); + + CalculateInterfaceMTU(); + + return true; + } + + previous = current; + current = current->Next(); + } + + return false; +} + + +int32 +PPPInterface::CountEncapsulators() const +{ + PPPEncapsulator *encapsulator = FirstEncapsulator(); + int32 count = 0; + for(; encapsulator; encapsulator = encapsulator->Next()) + ++count; + + return count; +} + + +PPPEncapsulator* +PPPInterface::EncapsulatorAt(int32 index) const +{ + PPPEncapsulator *encapsulator = FirstEncapsulator(); + int32 currentIndex = 0; + for(; encapsulator; encapsulator = encapsulator->Next(), ++currentIndex) + if(currentIndex == index) + return encapsulator; + + return NULL; +} + + +PPPEncapsulator* +PPPInterface::EncapsulatorFor(uint16 protocol, PPPEncapsulator *start = NULL) const +{ + PPPEncapsulator *current = start ? start : fFirstEncapsulator; + + for(; current; current = current->Next()) { + if(current->Protocol() == protocol + || (current->Flags() & PPP_INCLUDES_NCP + && current->Protocol() & 0x7FFF == protocol & 0x7FFF)) + return current; + } + + return current; +} + + +bool +PPPInterface::AddChild(PPPInterface *child) +{ + if(!child) + return false; + + LockerHelper locker(fLock); + + if(fChildren.HasItem(child) || !fChildren.AddItem(child)) + return false; + + child->SetParent(this); + + return true; +} + + +bool +PPPInterface::RemoveChild(PPPInterface *child) +{ + LockerHelper locker(fLock); + + if(!fChildren.RemoveItem(child)) + return false; + + child->SetParent(NULL); + + // parents cannot exist without their children + if(CountChildren() == 0 && fManager && Ifnet()) + Delete(); + + return true; +} + + +PPPInterface* +PPPInterface::ChildAt(int32 index) const +{ + PPPInterface *child = fChildren.ItemAt(index); + + if(child == fChildren.GetDefaultItem()) + return NULL; + + return child; +} + + +void +PPPInterface::SetAutoRedial(bool autoRedial = true) +{ + if(Mode() == PPP_CLIENT_MODE) + return; + + LockerHelper locker(fLock); + + fAutoRedial = autoRedial; +} + + +void +PPPInterface::SetDialOnDemand(bool dialOnDemand = true) +{ + // All protocols must check if DialOnDemand was enabled/disabled after this + // interface went down. This is the only situation where a change is relevant. + + // Only clients support DialOnDemand. Maybe no change needed. + if(Mode() != PPP_CLIENT_MODE || DoesDialOnDemand() == dialOnDemand) + return; + + LockerHelper locker(fLock); + + fDialOnDemand = dialOnDemand; + + // Do not allow changes when we are disconnected (only main interfaces). + // This would make no sense because + // - enabling: this cannot happen because hidden interfaces are deleted if they + // could not establish a connection (the user cannot access hidden interfaces) + // - disabling: the interface disappears as seen from the user, so we delete it + if(!Parent() && State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) { + if(!dialOnDemand) + Delete(); + // as long as the protocols were not configured we can just delete us + + return; + } + + // check if we need to register/unregister + if(dialOnDemand) { + RegisterInterface(); + if(Ifnet()) + Ifnet()->if_flags |= IFF_RUNNING; + } else if(!dialOnDemand && Phase() < PPP_ESTABLISHED_PHASE) { + UnregisterInterface(); + + // if we are already down we must delete us + if(State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) + Delete(); + } +} + + +bool +PPPInterface::SetPFCOptions(uint8 pfcOptions) +{ + if(PFCOptions() & PPP_FREEZE_PFC_OPTIONS) + return false; + + fPFCOptions = pfcOptions; + return true; +} + + +bool +PPPInterface::Up() +{ + if(InitCheck() != B_OK || Phase() == PPP_TERMINATION_PHASE) + return false; + + if(IsUp()) + return true; + + 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. + // Lock needs timeout because destructor could have locked the interface. + while(!fLock.LockWithTimeout(100000) != B_NO_ERROR) + if(fDeleteCounter > 0) + return false; + if(fUpThread == -1) + fUpThread = me; + + ReportManager().EnableReports(PPP_CONNECTION_REPORT, me, PPP_WAIT_FOR_REPLY); + fLock.Unlock(); + + // fUpThread tells the state machine to go up + if(me == fUpThread || me == fRedialThread) + StateMachine().OpenEvent(); + + if(me == fRedialThread && me != fUpThread) + return true; + // the redial thread is doing a DialRetry in this case + + while(true) { + if(IsUp()) { + // lock needs timeout because destructor could have locked the interface + while(!fLock.LockWithTimeout(100000) != B_NO_ERROR) + if(fDeleteCounter > 0) + return true; + + if(me == fUpThread) { + fDialRetry = 0; + fUpThread = -1; + } + + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + fLock.Unlock(); + + return true; + } + + // A wrong code usually happens when the redial thread gets notified + // of a Down() request. In that case a report will follow soon, so + // this can be ignored. + if(receive_data(&sender, &report, sizeof(report)) != PPP_REPORT_CODE) + continue; + + if(IsUp()) { + if(me == fUpThread) { + fDialRetry = 0; + fUpThread = -1; + } + + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } + + if(report.type == PPP_DESTRUCTION_REPORT) { + if(me == fUpThread) { + fDialRetry = 0; + fUpThread = -1; + } + + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } else if(report.type != PPP_CONNECTION_REPORT) { + PPP_REPLY(sender, B_OK); + continue; + } + + if(report.code == PPP_REPORT_GOING_UP) { + PPP_REPLY(sender, B_OK); + continue; + } else if(report.code == PPP_REPORT_UP_SUCCESSFUL) { + if(me == fUpThread) { + fDialRetry = 0; + fUpThread = -1; + send_data_with_timeout(fRedialThread, 0, NULL, 0, 200); + // notify redial thread that we do not need it anymore + } + + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return true; + } else if(report.code == PPP_REPORT_DOWN_SUCCESSFUL + || report.code == PPP_REPORT_UP_ABORTED + || report.code == PPP_REPORT_LOCAL_AUTHENTICATION_FAILED + || report.code == PPP_REPORT_PEER_AUTHENTICATION_FAILED) { + if(me == fUpThread) { + fDialRetry = 0; + fUpThread = -1; + + if(!DoesDialOnDemand() && report.code != PPP_REPORT_DOWN_SUCCESSFUL) + Delete(); + } + + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } + + if(me != fUpThread) { + // I am an observer + if(report.code == PPP_REPORT_DEVICE_UP_FAILED) { + if(fDialRetry >= fDialRetriesLimit || fUpThread == -1) { + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } else { + PPP_REPLY(sender, B_OK); + continue; + } + } else if(report.code == PPP_REPORT_CONNECTION_LOST) { + if(DoesAutoRedial()) { + PPP_REPLY(sender, B_OK); + continue; + } else { + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } + } + } else { + // I am the thread for the real task + if(report.code == PPP_REPORT_DEVICE_UP_FAILED) { + if(fDialRetry >= fDialRetriesLimit) { + fDialRetry = 0; + fUpThread = -1; + + if(!DoesDialOnDemand() + && report.code != PPP_REPORT_DOWN_SUCCESSFUL) + Delete(); + + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + return false; + } else { + ++fDialRetry; + PPP_REPLY(sender, B_OK); + Redial(DialRetryDelay()); + continue; + } + } else if(report.code == PPP_REPORT_CONNECTION_LOST) { + // the state machine knows that we are going up and leaves + // the redial task to us + if(DoesAutoRedial() && fDialRetry < fDialRetriesLimit) { + ++fDialRetry; + PPP_REPLY(sender, B_OK); + Redial(DialRetryDelay()); + continue; + } else { + fDialRetry = 0; + fUpThread = -1; + PPP_REPLY(sender, B_OK); + ReportManager().DisableReports(PPP_CONNECTION_REPORT, me); + + if(!DoesDialOnDemand() + && report.code != PPP_REPORT_DOWN_SUCCESSFUL) + Delete(); + + return false; + } + } + } + + // if the code is unknown we continue + PPP_REPLY(sender, B_OK); + } + + return false; +} + + +bool +PPPInterface::Down() +{ + if(InitCheck() != B_OK) + return false; + + send_data_with_timeout(fRedialThread, 0, NULL, 0, 200); + // the redial thread should be notified that the user wants to disconnect + + // this locked section guarantees that there are no state changes before we + // enable the connection reports + LockerHelper locker(fLock); + if(State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) + return true; + + ReportManager().EnableReports(PPP_CONNECTION_REPORT, find_thread(NULL)); + locker.UnlockNow(); + + thread_id sender; + ppp_report_packet report; + + StateMachine().CloseEvent(); + + while(true) { + if(receive_data(&sender, &report, sizeof(report)) != PPP_REPORT_CODE) + continue; + + if(report.type == PPP_DESTRUCTION_REPORT) + return true; + + if(report.type != PPP_CONNECTION_REPORT) + continue; + + if(report.code == PPP_REPORT_DOWN_SUCCESSFUL + || report.code == PPP_REPORT_UP_ABORTED + || (State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE)) { + ReportManager().DisableReports(PPP_CONNECTION_REPORT, find_thread(NULL)); + break; + } + } + + if(!DoesDialOnDemand()) + Delete(); + + return true; +} + + +bool +PPPInterface::IsUp() const +{ + LockerHelper locker(fLock); + + return Phase() == PPP_ESTABLISHED_PHASE; +} + + +bool +PPPInterface::LoadModules(driver_settings *settings, int32 start, int32 count) +{ + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + ppp_module_key_type type; + // which type key was used for loading this module? + + const char *name = NULL; + + // multilink handling + for(int32 index = start; + index < settings->parameter_count && index < start + count; index++) { + if(!strcasecmp(settings->parameters[index].name, PPP_MULTILINK_KEY) + && settings->parameters[index].value_count > 0) { + if(!LoadModule(settings->parameters[index].values[0], + settings->parameters[index].parameters, PPP_MULTILINK_KEY_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 + fManager->create_interface(settings, ID()); + return true; + } + + for(int32 index = start; + index < settings->parameter_count && index < start + count; index++) { + type = PPP_UNDEFINED_KEY_TYPE; + + name = settings->parameters[index].name; + + if(!strcasecmp(name, PPP_LOAD_MODULE_KEY)) + type = PPP_LOAD_MODULE_KEY_TYPE; + else if(!strcasecmp(name, PPP_DEVICE_KEY)) + type = PPP_DEVICE_KEY_TYPE; + else if(!strcasecmp(name, PPP_PROTOCOL_KEY)) + type = PPP_PROTOCOL_KEY_TYPE; + else if(!strcasecmp(name, PPP_AUTHENTICATOR_KEY)) + type = PPP_AUTHENTICATOR_KEY_TYPE; + + if(type >= 0) + for(int32 value_id = 0; value_id < settings->parameters[index].value_count; + value_id++) + if(!LoadModule(settings->parameters[index].values[value_id], + settings->parameters[index].parameters, type)) + return false; + } + + return true; +} + + +bool +PPPInterface::LoadModule(const char *name, driver_parameter *parameter, + ppp_module_key_type type) +{ + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + if(!name || strlen(name) > B_FILE_NAME_LENGTH) + return false; + + char *module_name = (char*) malloc(B_PATH_NAME_LENGTH); + + sprintf(module_name, "%s/%s", PPP_MODULES_PATH, name); + + ppp_module_info *module; + if(get_module(module_name, (module_info**) &module) != B_OK) + return false; + + // add the module to the list of loaded modules + // for putting them on our destruction + fModules.AddItem(module_name); + + return module->add_to(Parent()?*Parent():*this, this, parameter, type); +} + + +status_t +PPPInterface::Send(struct mbuf *packet, uint16 protocol) +{ + if(!packet) + return B_ERROR; + + // we must pass the basic tests! + if(InitCheck() != B_OK) { + m_freem(packet); + return B_ERROR; + } + + // test whether are going down + if(Phase() == PPP_TERMINATION_PHASE) { + m_freem(packet); + return B_ERROR; + } + + // go up if DialOnDemand enabled and we are down + if(DoesDialOnDemand() && Phase() == PPP_DOWN_PHASE) + Up(); + + // find the protocol handler for the current protocol number + int32 index = 0; + PPPProtocol *sending_protocol = ProtocolFor(protocol, &index); + while(sending_protocol && !sending_protocol->IsEnabled()) + sending_protocol = ProtocolFor(protocol, &(++index)); + + // Check if we must encapsulate the packet. + // We do not necessarily need a handler for 'protocol'. + // In such a case we still encapsulate the packet. + // Protocols which have the PPP_ALWAYS_ALLOWED flag set are never + // encapsulated. + if(sending_protocol && sending_protocol->Flags() & PPP_ALWAYS_ALLOWED + && is_handler_allowed(*sending_protocol, State(), Phase())) { + fIdleSince = real_time_clock(); + return SendToDevice(packet, protocol); + } + + // try the same for the encapsulator handler + PPPEncapsulator *sending_encapsulator = EncapsulatorFor(protocol); + while(sending_encapsulator && sending_encapsulator->Next() + && !sending_encapsulator->IsEnabled()) + sending_encapsulator = sending_encapsulator->Next() ? + EncapsulatorFor(protocol, sending_encapsulator->Next()) : NULL; + + if(sending_encapsulator && sending_encapsulator->Flags() & PPP_ALWAYS_ALLOWED + && is_handler_allowed(*sending_encapsulator, State(), Phase())) { + fIdleSince = real_time_clock(); + return SendToDevice(packet, protocol); + } + + // never send normal protocols when we are not up + if(!IsUp()) { + m_freem(packet); + return B_ERROR; + } + + fIdleSince = real_time_clock(); + + // send to next up encapsulator + if(!fFirstEncapsulator) + return SendToDevice(packet, protocol); + + if(!fFirstEncapsulator->IsEnabled()) + return fFirstEncapsulator->SendToNext(packet, protocol); + + if(is_handler_allowed(*fFirstEncapsulator, State(), Phase())) + return fFirstEncapsulator->Send(packet, protocol); + + m_freem(packet); + return B_ERROR; +} + + +status_t +PPPInterface::Receive(struct mbuf *packet, uint16 protocol) +{ + if(!packet) + return B_ERROR; + + int32 result = PPP_REJECTED; + // assume we have no handler + + // Set our interface as the receiver. + // The real netstack protocols (IP, IPX, etc.) might get confused if our + // interface is a main interface and at the same time is not registered + // because then there is no receiver interface. + // PPP NCPs should be aware of that! + if(packet->m_flags & M_PKTHDR && Ifnet() != NULL) + packet->m_pkthdr.rcvif = Ifnet(); + + // Find handler and let it parse the packet. + // The handler does need not be up because if we are a server + // the handler might be upped by this packet. + PPPEncapsulator *encapsulator_handler = EncapsulatorFor(protocol); + for(; encapsulator_handler; + encapsulator_handler = + encapsulator_handler->Next() ? + EncapsulatorFor(protocol, encapsulator_handler->Next()) : NULL) { + if(!encapsulator_handler->IsEnabled() + || !is_handler_allowed(*encapsulator_handler, State(), Phase())) + continue; + // skip handler if disabled or not allowed + + result = encapsulator_handler->Receive(packet, protocol); + if(result == PPP_UNHANDLED) + continue; + + return result; + } + + // no encapsulator handler could be found; try a protocol handler + int32 index = 0; + PPPProtocol *protocol_handler = ProtocolFor(protocol, &index); + for(; protocol_handler; protocol_handler = ProtocolFor(protocol, &(++index))) { + if(!protocol_handler->IsEnabled() + || !is_handler_allowed(*protocol_handler, State(), Phase())) + continue; + // skip handler if disabled or not allowed + + result = protocol_handler->Receive(packet, protocol); + if(result == PPP_UNHANDLED) + continue; + + return result; + } + + // maybe the parent interface can handle the packet + if(Parent()) + return Parent()->Receive(packet, protocol); + + if(result == PPP_UNHANDLED) { + m_freem(packet); + return PPP_DISCARDED; + } else { + StateMachine().RUCEvent(packet, protocol); + return PPP_REJECTED; + } +} + + +status_t +PPPInterface::SendToDevice(struct mbuf *packet, uint16 protocol) +{ + if(!packet) + return B_ERROR; + + // we must pass the basic tests like: + // do we have a device? + // did we load all modules? + if(InitCheck() != B_OK || !Device()) { + m_freem(packet); + return B_ERROR; + } + + // test whether are going down + if(Phase() == PPP_TERMINATION_PHASE) { + m_freem(packet); + return B_ERROR; + } + + // go up if DialOnDemand enabled and we are down + if(DoesDialOnDemand() + && (Phase() == PPP_DOWN_PHASE + || Phase() == PPP_ESTABLISHMENT_PHASE) + && !Up()) { + m_freem(packet); + return B_ERROR; + } + + // find the protocol handler for the current protocol number + int32 index = 0; + PPPProtocol *sending_protocol = ProtocolFor(protocol, &index); + while(sending_protocol && !sending_protocol->IsEnabled()) + sending_protocol = ProtocolFor(protocol, &(++index)); + + // make sure that protocol is allowed to send and everything is up + if(!Device()->IsUp() || !sending_protocol || !sending_protocol->IsEnabled() + || !is_handler_allowed(*sending_protocol, State(), Phase())){ + m_freem(packet); + return B_ERROR; + } + + // encode in ppp frame and consider using PFC + if(UseLocalPFC() && protocol & 0xFF00 == 0) { + M_PREPEND(packet, 1); + + if(packet == NULL) + return B_ERROR; + + uint8 *header = mtod(packet, uint8*); + *header = protocol & 0xFF; + } else { + M_PREPEND(packet, 2); + + if(packet == NULL) + return B_ERROR; + + // set protocol (the only header field) + protocol = htons(protocol); + uint16 *header = mtod(packet, uint16*); + *header = protocol; + } + + fIdleSince = real_time_clock(); + + // pass to device/children + if(!IsMultilink() || Parent()) { + // check if packet is too big for device + if((packet->m_flags & M_PKTHDR && (uint32) packet->m_pkthdr.len > MRU()) + || packet->m_len > MRU()) { + m_freem(packet); + return B_ERROR; + } + + return Device()->Send(packet); + } else { + // the multilink encapsulator should have sent it to some child interface + m_freem(packet); + return B_ERROR; + } +} + + +status_t +PPPInterface::ReceiveFromDevice(struct mbuf *packet) +{ + if(!packet) + return B_ERROR; + + if(InitCheck() != B_OK) { + m_freem(packet); + return B_ERROR; + } + + // decode ppp frame and recognize PFC + uint16 protocol = *mtod(packet, uint8*); + if(protocol == 0) + m_adj(packet, 1); + else { + protocol = ntohs(*mtod(packet, uint16*)); + m_adj(packet, 2); + } + + return Receive(packet, protocol); +} + + +void +PPPInterface::Pulse() +{ + if(fDeleteCounter > 0) + return; + // we have no pulse when we are dead ;) + + // check our idle time and disconnect if needed + if(fDisconnectAfterIdleSince > 0 && fIdleSince != 0 + && fIdleSince - real_time_clock() >= fDisconnectAfterIdleSince) { + StateMachine().CloseEvent(); + return; + } + + if(Device()) + Device()->Pulse(); + + for(int32 index = 0; index < CountProtocols(); index++) + ProtocolAt(index)->Pulse(); + + PPPEncapsulator *encapsulator = fFirstEncapsulator; + for(; encapsulator; encapsulator = encapsulator->Next()) + encapsulator->Pulse(); +} + + +bool +PPPInterface::RegisterInterface() +{ + if(fIfnet) + return true; + // we are already registered + + LockerHelper locker(fLock); + + if(InitCheck() != B_OK) + return false; + // we cannot register if something is wrong + + // only MainInterfaces get an ifnet + if(IsMultilink() && Parent() && Parent()->RegisterInterface()) + return true; + + if(!fManager) + return false; + + fIfnet = fManager->register_interface(ID()); + + if(!fIfnet) + return false; + + CalculateBaudRate(); + + return true; +} + + +bool +PPPInterface::UnregisterInterface() +{ + if(!fIfnet) + return true; + // we are already unregistered + + LockerHelper locker(fLock); + + // only MainInterfaces get an ifnet + if(IsMultilink() && Parent()) + return true; + + if(!fManager) + return false; + + fManager->unregister_interface(ID()); + fIfnet = NULL; + + return true; +} + + +// called by PPPManager: manager routes stack ioctls to interface +status_t +PPPInterface::StackControl(uint32 op, void *data) +{ + // TODO: + // implement when writing ppp_manager module + + switch(op) { + default: + return StackControlEachHandler(op, data); + } + + return B_OK; +} + + +// used by ControlEachHandler() +template +class CallStackControl { + public: + inline CallStackControl(uint32 op, void *data, status_t& result) + : fOp(op), fData(data), fResult(result) {} + inline void operator() (T *item) + { + if(!item || !item->IsEnabled()) + return; + status_t tmp = item->StackControl(fOp, fData); + if(tmp == B_OK && fResult == B_BAD_VALUE) + fResult = B_OK; + else if(tmp != B_BAD_VALUE) + fResult = tmp; + } + private: + uint32 fOp; + void *fData; + status_t& fResult; +}; + +// This calls Control() with the given parameters for each handler. +// Return values: +// B_OK: all handlers returned B_OK +// B_BAD_VALUE: no handler was found +// any other value: the error value that was returned by the last handler that failed +status_t +PPPInterface::StackControlEachHandler(uint32 op, void *data) +{ + status_t result = B_BAD_VALUE, tmp; + + PPPEncapsulator *encapsulator = FirstEncapsulator(); + for(; encapsulator; encapsulator = encapsulator->Next()) { + tmp = encapsulator->StackControl(op, data); + if(tmp == B_OK && result == B_BAD_VALUE) + result = B_OK; + else if(tmp != B_BAD_VALUE) + result = tmp; + } + + ForEachItem(fProtocols, CallStackControl(op, data, result)); + ForEachItem(LCP().fLCPExtensions, + CallStackControl(op, data, result)); + ForEachItem(LCP().fOptionHandlers, + CallStackControl(op, data, result)); + + return result; +} + + +void +PPPInterface::CalculateInterfaceMTU() +{ + fInterfaceMTU = fMRU; + + // sum all headers (the protocol field is not counted) + PPPEncapsulator *encapsulator = fFirstEncapsulator; + for(; encapsulator; encapsulator = encapsulator->Next()) + fHeaderLength += encapsulator->Overhead(); + + fInterfaceMTU -= fHeaderLength; + + if(Ifnet()) { + Ifnet()->if_mtu = fInterfaceMTU; + Ifnet()->if_hdrlen = fHeaderLength; + } + + if(Parent()) + Parent()->CalculateInterfaceMTU(); +} + + +void +PPPInterface::CalculateBaudRate() +{ + if(!Ifnet()) + return; + + if(Device()) + fIfnet->if_baudrate = max_c(Device()->InputTransferRate(), + Device()->OutputTransferRate()); + else { + fIfnet->if_baudrate = 0; + for(int32 index = 0; index < CountChildren(); index++) + if(ChildAt(index)->Ifnet()) + fIfnet->if_baudrate += ChildAt(index)->Ifnet()->if_baudrate; + } +} + + +void +PPPInterface::Redial(uint32 delay) +{ + if(fRedialThread != -1) + return; + + // start a new thread that calls our Up() method + redial_info info; + info.interface = this; + info.thread = &fRedialThread; + info.delay = delay; + + fRedialThread = spawn_thread(redial_thread, "PPPInterface: redial_thread", + B_NORMAL_PRIORITY, NULL); + + resume_thread(fRedialThread); + + send_data(fRedialThread, 0, &info, sizeof(redial_info)); +} + + +status_t +redial_thread(void *data) +{ + redial_info info; + thread_id sender; + int32 code; + + receive_data(&sender, &info, sizeof(redial_info)); + + // we try to receive data instead of snooze, so we can quit on destruction + if(receive_data_with_timeout(&sender, &code, NULL, 0, info.delay) == B_OK) { + *info.thread = -1; + info.interface->Report(PPP_CONNECTION_REPORT, PPP_REPORT_UP_ABORTED, NULL, 0); + return B_OK; + } + + info.interface->Up(); + *info.thread = -1; + + return B_OK; +} + + +status_t +in_queue_thread(void *data) +{ + PPPInterface *interface = (PPPInterface*) data; + struct ifq *queue = interface->InQueue(); + struct mbuf *packet; + status_t error; + + while(true) { + error = acquire_sem_etc(queue->pop, 1, B_CAN_INTERRUPT | B_DO_NOT_RESCHEDULE, 0); + + if(error == B_INTERRUPTED) + continue; + else if(error != B_NO_ERROR) + break; + + IFQ_DEQUEUE(queue, packet); + + if(packet) + interface->ReceiveFromDevice(packet); + } + + return B_ERROR; +} + + +// ---------------------------------- +// Function: interface_deleter_thread +// ---------------------------------- +// The destructor is private, so this thread function cannot delete our interface. +// To solve this problem we create a 'fake' class PPPManager (friend of PPPInterface) +// which is only defined here (the real class is defined in the ppp_manager module). +class PPPManager { + public: + PPPManager(PPPInterface *interface) + { if(interface) delete interface; } +}; + +status_t +interface_deleter_thread(void *data) +{ + PPPManager((PPPInterface*) data); + + return B_OK; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp new file mode 100644 index 0000000000..ae76c7a9d7 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCP.cpp @@ -0,0 +1,325 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + + +PPPLCP::PPPLCP(PPPInterface& interface) + : PPPProtocol("LCP", PPP_ESTABLISHMENT_PHASE, PPP_LCP_PROTOCOL, + AF_UNSPEC, interface, NULL, PPP_ALWAYS_ALLOWED), + fStateMachine(interface.StateMachine()), + fTarget(NULL) +{ + SetUpRequested(false); + // the state machine does everything for us +} + + +PPPLCP::~PPPLCP() +{ + while(CountOptionHandlers()) + delete OptionHandlerAt(0); +} + + +bool +PPPLCP::AddOptionHandler(PPPOptionHandler *handler) +{ + if(!handler) + return false; + + LockerHelper locker(StateMachine().Locker()); + + if(Phase() != PPP_DOWN_PHASE || OptionHandlerFor(handler->Type())) + return false; + // a running connection may not change and there may only be + // one handler per option type + + return fOptionHandlers.AddItem(handler); +} + + +bool +PPPLCP::RemoveOptionHandler(PPPOptionHandler *handler) +{ + LockerHelper locker(StateMachine().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.GetDefaultItem()) + return NULL; + + return handler; +} + + +PPPOptionHandler* +PPPLCP::OptionHandlerFor(uint8 type, int32 *start = NULL) const +{ + // The iteration style in this method is strange C/C++. + // Explanation: I use this style because it makes extending all XXXFor + // methods simpler as that they look very similar, now. + + int32 index = start ? *start : 0; + + if(index < 0) + return NULL; + + PPPOptionHandler *current = OptionHandlerAt(index); + + for(; current; current = OptionHandlerAt(++index)) { + if(current->Type() == type) { + if(start) + *start = index; + return current; + } + } + + return NULL; +} + + +bool +PPPLCP::AddLCPExtension(PPPLCPExtension *extension) +{ + if(!extension) + return false; + + LockerHelper locker(StateMachine().Locker()); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + return fLCPExtensions.AddItem(extension); +} + + +bool +PPPLCP::RemoveLCPExtension(PPPLCPExtension *extension) +{ + LockerHelper locker(StateMachine().Locker()); + + if(Phase() != PPP_DOWN_PHASE) + return false; + // a running connection may not change + + return fLCPExtensions.RemoveItem(extension); +} + + +PPPLCPExtension* +PPPLCP::LCPExtensionAt(int32 index) const +{ + PPPLCPExtension *extension = fLCPExtensions.ItemAt(index); + + if(extension == fLCPExtensions.GetDefaultItem()) + return NULL; + + return extension; +} + + +PPPLCPExtension* +PPPLCP::LCPExtensionFor(uint8 code, int32 *start = NULL) const +{ + // The iteration style in this method is strange C/C++. + // Explanation: I use this style because it makes extending all XXXFor + // methods simpler as that they look very similar, now. + + int32 index = start ? *start : 0; + + if(index < 0) + return NULL; + + PPPLCPExtension *current = LCPExtensionAt(index); + + for(; current; current = LCPExtensionAt(++index)) { + if(current->Code() == code) { + if(start) + *start = index; + return current; + } + } + + return NULL; +} + + +uint32 +PPPLCP::AdditionalOverhead() const +{ + uint32 overhead = 0; + + if(Target()) + overhead += Target()->Overhead(); + + return overhead; +} + + +bool +PPPLCP::Up() +{ + return true; +} + + +bool +PPPLCP::Down() +{ + return true; +} + + +status_t +PPPLCP::Send(struct mbuf *packet) +{ + if(Target()) + return Target()->Send(packet, PPP_LCP_PROTOCOL); + else + return Interface().Send(packet, PPP_LCP_PROTOCOL); +} + + +status_t +PPPLCP::Receive(struct mbuf *packet, uint16 protocol) +{ + if(!packet) + return B_ERROR; + + if(protocol != PPP_LCP_PROTOCOL) + return PPP_UNHANDLED; + + ppp_lcp_packet *data = mtod(packet, ppp_lcp_packet*); + + // adjust length (remove padding) + int32 length = packet->m_len; + if(packet->m_flags & M_PKTHDR) + length = packet->m_pkthdr.len; + if(length - ntohs(data->length) != 0) + m_adj(packet, length); + + struct mbuf *copy = m_gethdr(MT_DATA); + if(copy) { + copy->m_data += AdditionalOverhead(); + copy->m_len = packet->m_len; + memcpy(copy->m_data, packet->m_data, copy->m_len); + } + + if(ntohs(data->length) < 4) + return B_ERROR; + + bool handled = true; + + switch(data->code) { + case PPP_CONFIGURE_REQUEST: + StateMachine().RCREvent(packet); + break; + + case PPP_CONFIGURE_ACK: + StateMachine().RCAEvent(packet); + break; + + case PPP_CONFIGURE_NAK: + case PPP_CONFIGURE_REJECT: + StateMachine().RCNEvent(packet); + break; + + case PPP_TERMINATE_REQUEST: + StateMachine().RTREvent(packet); + break; + + case PPP_TERMINATE_ACK: + StateMachine().RTAEvent(packet); + break; + + case PPP_CODE_REJECT: + StateMachine().RXJEvent(packet); + break; + + case PPP_PROTOCOL_REJECT: + StateMachine().RXJEvent(packet); + break; + + case PPP_ECHO_REQUEST: + case PPP_ECHO_REPLY: + case PPP_DISCARD_REQUEST: + StateMachine().RXREvent(packet); + break; + + default: + m_freem(packet); + handled = false; + } + + packet = copy; + + if(!packet) + return handled ? B_OK : B_ERROR; + + status_t result = B_OK; + + // Try to find LCP extensions that can handle this code. + // We must duplicate the packet in order to ask all handlers. + int32 index = 0; + PPPLCPExtension *extension = LCPExtensionFor(data->code, &index); + for(; extension; extension = LCPExtensionFor(data->code, &(++index))) { + if(!extension->IsEnabled()) + continue; + + result = extension->Receive(packet, data->code); + + // check return value and return it on error + if(result == B_OK) + handled = true; + else if(result != PPP_UNHANDLED) { + m_freem(packet); + return result; + } + } + + if(!handled) { + StateMachine().RUCEvent(packet, PPP_LCP_PROTOCOL, PPP_CODE_REJECT); + return PPP_REJECTED; + } + + m_freem(packet); + + return result; +} + + +void +PPPLCP::Pulse() +{ + StateMachine().TimerEvent(); + + for(int32 index = 0; index < CountLCPExtensions(); index++) + LCPExtensionAt(index)->Pulse(); +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCPExtension.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCPExtension.cpp new file mode 100644 index 0000000000..65ba15d444 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPLCPExtension.cpp @@ -0,0 +1,97 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include + +#include + + +PPPLCPExtension::PPPLCPExtension(const char *name, uint8 code, PPPInterface& interface, + driver_parameter *settings) + : fInterface(interface), + fSettings(settings), + fCode(code), + fEnabled(true) +{ + if(name) + fName = strdup(name); + else + fName = strdup("Unknown"); + + fInitStatus = interface.LCP().AddLCPExtension(this) ? B_OK : B_ERROR; +} + + +PPPLCPExtension::~PPPLCPExtension() +{ + free(fName); + + Interface().LCP().RemoveLCPExtension(this); +} + + +status_t +PPPLCPExtension::InitCheck() const +{ + return fInitStatus; +} + + +status_t +PPPLCPExtension::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_SIMPLE_HANDLER_INFO: { + if(length < sizeof(ppp_simple_handler_info_t) || !data) + return B_ERROR; + + ppp_simple_handler_info *info = (ppp_simple_handler_info*) data; + memset(info, 0, sizeof(ppp_simple_handler_info_t)); + strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); + info->settings = Settings(); + info->isEnabled = IsEnabled(); + } break; + + case PPPC_SET_ENABLED: + if(length < sizeof(uint32) || !data) + return B_ERROR; + + SetEnabled(*((uint32*)data)); + break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPLCPExtension::StackControl(uint32 op, void *data) +{ + switch(op) { + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +void +PPPLCPExtension::Reset() +{ + // do nothing by default +} + + +void +PPPLCPExtension::Pulse() +{ + // do nothing by default +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPOptionHandler.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPOptionHandler.cpp new file mode 100644 index 0000000000..f1f0d7cbb6 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPOptionHandler.cpp @@ -0,0 +1,83 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include + +#include + + +PPPOptionHandler::PPPOptionHandler(const char *name, uint8 type, + PPPInterface& interface, driver_parameter *settings) + : fType(type), + fInterface(interface), + fSettings(settings), + fEnabled(true) +{ + if(name) + fName = strdup(name); + else + fName = strdup("Unknown"); + + fInitStatus = interface.LCP().AddOptionHandler(this) ? B_OK : B_ERROR; +} + + +PPPOptionHandler::~PPPOptionHandler() +{ + free(fName); + + Interface().LCP().RemoveOptionHandler(this); +} + + +status_t +PPPOptionHandler::InitCheck() const +{ + return fInitStatus; +} + + +status_t +PPPOptionHandler::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_SIMPLE_HANDLER_INFO: { + if(length < sizeof(ppp_simple_handler_info_t) || !data) + return B_ERROR; + + ppp_simple_handler_info *info = (ppp_simple_handler_info*) data; + memset(info, 0, sizeof(ppp_simple_handler_info_t)); + strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); + info->settings = Settings(); + info->isEnabled = IsEnabled(); + } break; + + case PPPC_SET_ENABLED: + if(length < sizeof(uint32) || !data) + return B_ERROR; + + SetEnabled(*((uint32*)data)); + break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPOptionHandler::StackControl(uint32 op, void *data) +{ + switch(op) { + default: + return B_BAD_VALUE; + } + + return B_OK; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPProtocol.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPProtocol.cpp new file mode 100644 index 0000000000..37b4020d45 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPProtocol.cpp @@ -0,0 +1,180 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include + +#include +#include "settings_tools.h" + +#include + + +PPPProtocol::PPPProtocol(const char *name, ppp_phase phase, uint16 protocol, + int32 addressFamily, PPPInterface& interface, + driver_parameter *settings, int32 flags = PPP_NO_FLAGS, + const char *type = NULL, PPPOptionHandler *optionHandler = NULL) + : fPhase(phase), + fProtocol(protocol), + fAddressFamily(addressFamily), + fInterface(interface), + fSettings(settings), + fFlags(flags), + fOptionHandler(optionHandler), + fEnabled(true), + fUpRequested(true), + fConnectionStatus(PPP_DOWN_PHASE) +{ + if(name) + fName = strdup(name); + else + fName = strdup("Unknown"); + + if(type) + fType = strdup(type); + else + fType = strdup("Unknown"); + + const char *sideString = get_parameter_value("side", settings); + if(sideString) + fSide = get_side_string_value(sideString, PPP_LOCAL_SIDE); + else { + if(interface.Mode() == PPP_CLIENT_MODE) + fSide = PPP_LOCAL_SIDE; + else + fSide = PPP_PEER_SIDE; + } + + fInitStatus = interface.AddProtocol(this) ? B_OK : B_ERROR; +} + + +PPPProtocol::~PPPProtocol() +{ + free(fName); + free(fType); + + Interface().RemoveProtocol(this); +} + + +status_t +PPPProtocol::InitCheck() const +{ + return fInitStatus; +} + + +void +PPPProtocol::SetEnabled(bool enabled = true) +{ + fEnabled = enabled; + + if(!enabled) { + if(IsUp() || IsGoingUp()) + Down(); + } else if(!IsUp() && !IsGoingUp() && IsUpRequested() && Interface().IsUp()) + Up(); +} + + +status_t +PPPProtocol::Control(uint32 op, void *data, size_t length) +{ + switch(op) { + case PPPC_GET_HANDLER_INFO: { + if(length < sizeof(ppp_handler_info_t) || !data) + return B_ERROR; + + ppp_handler_info *info = (ppp_handler_info*) data; + memset(info, 0, sizeof(ppp_handler_info_t)); + strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); + info->settings = Settings(); + info->phase = Phase(); + info->addressFamily = AddressFamily(); + info->flags = Flags(); + info->side = Side(); + info->protocol = Protocol(); + info->isEnabled = IsEnabled(); + info->isUpRequested = IsUpRequested(); + info->connectionStatus = fConnectionStatus; + strncpy(info->type, Type(), PPP_HANDLER_NAME_LENGTH_LIMIT); + } break; + + case PPPC_SET_ENABLED: + if(length < sizeof(uint32) || !data) + return B_ERROR; + + SetEnabled(*((uint32*)data)); + break; + + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +status_t +PPPProtocol::StackControl(uint32 op, void *data) +{ + switch(op) { + default: + return B_BAD_VALUE; + } + + return B_OK; +} + + +void +PPPProtocol::Pulse() +{ + // do nothing by default +} + + +void +PPPProtocol::UpStarted() +{ + fConnectionStatus = PPP_ESTABLISHMENT_PHASE; +} + + +void +PPPProtocol::DownStarted() +{ + fConnectionStatus = PPP_TERMINATION_PHASE; +} + + +void +PPPProtocol::UpFailedEvent() +{ + fConnectionStatus = PPP_DOWN_PHASE; + + Interface().StateMachine().UpFailedEvent(this); +} + + +void +PPPProtocol::UpEvent() +{ + fConnectionStatus = PPP_ESTABLISHED_PHASE; + + Interface().StateMachine().UpEvent(this); +} + + +void +PPPProtocol::DownEvent() +{ + fConnectionStatus = PPP_DOWN_PHASE; + + Interface().StateMachine().DownEvent(this); +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPReportManager.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPReportManager.cpp new file mode 100644 index 0000000000..2a03c69dfb --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPReportManager.cpp @@ -0,0 +1,148 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include + +#include + +#include + + +PPPReportManager::PPPReportManager(BLocker& lock) + : fLock(lock) +{ +} + + +PPPReportManager::~PPPReportManager() +{ + for(int32 index = 0; index < fReportRequests.CountItems(); index++) + delete fReportRequests.ItemAt(index); +} + + +void +PPPReportManager::EnableReports(ppp_report_type type, thread_id thread, + int32 flags = PPP_NO_FLAGS) +{ + LockerHelper locker(fLock); + + ppp_report_request *request = new ppp_report_request; + request->type = type; + request->thread = thread; + request->flags = flags; + + fReportRequests.AddItem(request); +} + + +void +PPPReportManager::DisableReports(ppp_report_type type, thread_id thread) +{ + LockerHelper locker(fLock); + + ppp_report_request *request; + + for(int32 i = 0; i < fReportRequests.CountItems(); i++) { + request = fReportRequests.ItemAt(i); + + if(request->thread != thread) + continue; + + if(request->type == type || request->type == PPP_ALL_REPORTS) + fReportRequests.RemoveItem(request); + } +} + + +bool +PPPReportManager::DoesReport(ppp_report_type type, thread_id thread) +{ + LockerHelper locker(fLock); + + ppp_report_request *request; + + for(int32 i = 0; i < fReportRequests.CountItems(); i++) { + request = fReportRequests.ItemAt(i); + + if(request->thread == thread && request->type == type) + return true; + } + + return false; +} + + +bool +PPPReportManager::Report(ppp_report_type type, int32 code, void *data, int32 length) +{ + if(length > PPP_REPORT_DATA_LIMIT) + return false; + + if(fReportRequests.CountItems() == 0) + return true; + + if(!data) + length = 0; + + LockerHelper locker(fLock); + + status_t result; + thread_id sender, me = find_thread(NULL); + bool acceptable = true; + + ppp_report_packet report; + report.type = type; + report.code = code; + report.length = length; + memcpy(report.data, data, length); + + ppp_report_request *request; + + for(int32 index = 0; index < fReportRequests.CountItems(); index++) { + request = fReportRequests.ItemAt(index); + + // do not send to yourself + if(request->thread == me) + continue; + + 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) { + fReportRequests.RemoveItem(request); + --index; + continue; + } else if(result == B_OK) { + if(request->flags & PPP_WAIT_FOR_REPLY) { + 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) + acceptable = false; + } + } + + if(request->flags & PPP_REMOVE_AFTER_REPORT) { + fReportRequests.RemoveItem(request); + --index; + } + } + + return acceptable; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPStateMachine.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPStateMachine.cpp new file mode 100644 index 0000000000..937eda7fce --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPStateMachine.cpp @@ -0,0 +1,1813 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define PPP_STATE_MACHINE_TIMEOUT 3000000 + + +PPPStateMachine::PPPStateMachine(PPPInterface& interface) + : fInterface(interface), + fLCP(interface.LCP()), + fPhase(PPP_DOWN_PHASE), + fState(PPP_INITIAL_STATE), + fID(system_time() & 0xFF), + fMagicNumber(0), + fLocalAuthenticationStatus(PPP_NOT_AUTHENTICATED), + fPeerAuthenticationStatus(PPP_NOT_AUTHENTICATED), + fLocalAuthenticationName(NULL), + fPeerAuthenticationName(NULL), + fMaxRequest(10), + fMaxTerminate(2), + fMaxNak(5), + fRequestID(0), + fTerminateID(0), + fEchoID(0), + fNextTimeout(0) +{ +} + + +PPPStateMachine::~PPPStateMachine() +{ + free(fLocalAuthenticationName); + free(fPeerAuthenticationName); +} + + +uint8 +PPPStateMachine::NextID() +{ + return (uint8) atomic_add(&fID, 1); +} + + +// remember: NewState() must always be called _after_ IllegalEvent() +// because IllegalEvent() also looks at the current state. +void +PPPStateMachine::NewState(ppp_state next) +{ + // maybe we do not need the timer anymore + if(next < PPP_CLOSING_STATE || next == PPP_OPENED_STATE) + fNextTimeout = 0; + + if(State() == PPP_OPENED_STATE && next != State()) + ResetLCPHandlers(); + + fState = next; +} + + +void +PPPStateMachine::NewPhase(ppp_phase next) +{ + // the ifnet's running flag is set here and in PPPInterface::SetDialOnDemand() + + // there is nothing after established phase and nothing before down phase + if(next > PPP_ESTABLISHED_PHASE) + next = PPP_ESTABLISHED_PHASE; + else if(next < PPP_DOWN_PHASE) + next = PPP_DOWN_PHASE; + + // Report a down event to parent if we are not usable anymore. + // The report threads get their notification later. + if(Phase() == PPP_ESTABLISHED_PHASE && next != Phase()) { + // there is no need to unset the running flag because we unregister, anyway + if(!Interface().DoesDialOnDemand()) + Interface().UnregisterInterface(); + + if(Interface().Parent()) + Interface().Parent()->StateMachine().DownEvent(Interface()); + } + + fPhase = next; + + if(Phase() == PPP_ESTABLISHED_PHASE) { + if(Interface().Ifnet() && !Interface().DoesDialOnDemand()) + Interface().Ifnet()->if_flags |= IFF_RUNNING; + + Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_UP_SUCCESSFUL, NULL, 0); + } +} + + +// public actions +bool +PPPStateMachine::Reconfigure() +{ + LockerHelper locker(fLock); + + if(State() < PPP_REQ_SENT_STATE) + return false; + + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + + DownProtocols(); + DownEncapsulators(); + ResetLCPHandlers(); + + locker.UnlockNow(); + + return SendConfigureRequest(); +} + + +bool +PPPStateMachine::SendEchoRequest() +{ + if(State() != PPP_OPENED_STATE) + return false; + + struct mbuf *packet = m_gethdr(MT_DATA); + if(!packet) + return false; + + packet->m_data += LCP().AdditionalOverhead(); + packet->m_len = 8; + // echo requests are at least eight bytes long + + ppp_lcp_packet *request = mtod(packet, ppp_lcp_packet*); + request->code = PPP_ECHO_REQUEST; + request->id = NextID(); + fEchoID = request->id; + request->length = htons(packet->m_len); + memcpy(request->data, &fMagicNumber, sizeof(fMagicNumber)); + + return LCP().Send(packet) == B_OK; +} + + +bool +PPPStateMachine::SendDiscardRequest() +{ + if(State() != PPP_OPENED_STATE) + return false; + + struct mbuf *packet = m_gethdr(MT_DATA); + if(!packet) + return false; + + packet->m_data += LCP().AdditionalOverhead(); + packet->m_len = 8; + // discard requests are at least eight bytes long + + ppp_lcp_packet *request = mtod(packet, ppp_lcp_packet*); + request->code = PPP_DISCARD_REQUEST; + request->id = NextID(); + request->length = htons(packet->m_len); + memcpy(request->data, &fMagicNumber, sizeof(fMagicNumber)); + + return LCP().Send(packet) == B_OK; +} + + +// authentication events +void +PPPStateMachine::LocalAuthenticationRequested() +{ + LockerHelper locker(fLock); + + fLocalAuthenticationStatus = PPP_AUTHENTICATING; + free(fLocalAuthenticationName); + fLocalAuthenticationName = NULL; +} + + +void +PPPStateMachine::LocalAuthenticationAccepted(const char *name) +{ + LockerHelper locker(fLock); + + fLocalAuthenticationStatus = PPP_AUTHENTICATION_SUCCESSFUL; + free(fLocalAuthenticationName); + fLocalAuthenticationName = strdup(name); + + Interface().Report(PPP_CONNECTION_REPORT, + PPP_REPORT_LOCAL_AUTHENTICATION_SUCCESSFUL, NULL, 0); +} + + +void +PPPStateMachine::LocalAuthenticationDenied(const char *name) +{ + LockerHelper locker(fLock); + + fLocalAuthenticationStatus = PPP_AUTHENTICATION_FAILED; + free(fLocalAuthenticationName); + fLocalAuthenticationName = strdup(name); +} + + +void +PPPStateMachine::PeerAuthenticationRequested() +{ + LockerHelper locker(fLock); + + fPeerAuthenticationStatus = PPP_AUTHENTICATING; + free(fPeerAuthenticationName); + fPeerAuthenticationName = NULL; +} + + +void +PPPStateMachine::PeerAuthenticationAccepted(const char *name) +{ + LockerHelper locker(fLock); + + fPeerAuthenticationStatus = PPP_AUTHENTICATION_SUCCESSFUL; + free(fPeerAuthenticationName); + fPeerAuthenticationName = strdup(name); + + Interface().Report(PPP_CONNECTION_REPORT, + PPP_REPORT_PEER_AUTHENTICATION_SUCCESSFUL, NULL, 0); +} + + +void +PPPStateMachine::PeerAuthenticationDenied(const char *name) +{ + LockerHelper locker(fLock); + + fPeerAuthenticationStatus = PPP_AUTHENTICATION_FAILED; + free(fPeerAuthenticationName); + fPeerAuthenticationName = strdup(name); + + CloseEvent(); +} + + +void +PPPStateMachine::UpFailedEvent(PPPInterface& interface) +{ + // TODO: + // log that an interface did not go up +} + + +void +PPPStateMachine::UpEvent(PPPInterface& interface) +{ + LockerHelper locker(fLock); + + if(Phase() <= PPP_TERMINATION_PHASE) { + interface.StateMachine().CloseEvent(); + return; + } + + Interface().CalculateBaudRate(); + + if(Phase() == PPP_ESTABLISHMENT_PHASE) { + // this is the first interface that went up + Interface().SetMRU(interface.MRU()); + locker.UnlockNow(); + ThisLayerUp(); + } else if(Interface().MRU() > interface.MRU()) + Interface().SetMRU(interface.MRU()); + // MRU should always be the smallest value of all children + + NewState(PPP_OPENED_STATE); +} + + +void +PPPStateMachine::DownEvent(PPPInterface& interface) +{ + LockerHelper locker(fLock); + + uint32 MRU = 0; + // the new MRU + + Interface().CalculateBaudRate(); + + // when all children are down we should not be running + if(Interface().IsMultilink() && !Interface().Parent()) { + uint32 count = 0; + PPPInterface *child; + for(int32 index = 0; index < Interface().CountChildren(); index++) { + child = Interface().ChildAt(index); + + if(child && child->IsUp()) { + // set MRU to the smallest value of all children + if(MRU == 0) + MRU = child->MRU(); + else if(MRU > child->MRU()) + MRU = child->MRU(); + + ++count; + } + } + + if(MRU == 0) + Interface().SetMRU(1500); + else + Interface().SetMRU(MRU); + + if(count == 0) { + locker.UnlockNow(); + DownEvent(); + } + } +} + + +void +PPPStateMachine::UpFailedEvent(PPPProtocol *protocol) +{ + if((protocol->Flags() & PPP_NOT_IMPORTANT) == 0) { + if(Interface().Mode() == PPP_CLIENT_MODE) { + // pretend we lost connection: + if(Interface().IsMultilink() && !Interface().Parent()) + for(int32 index = 0; index < Interface().CountChildren(); index++) + Interface().ChildAt(index)->StateMachine().CloseEvent(); + else if(Interface().Device()) + Interface().Device()->Down(); + else + CloseEvent(); + // just to be on the secure side ;) + } else + CloseEvent(); + } +} + + +void +PPPStateMachine::UpEvent(PPPProtocol *protocol) +{ + LockerHelper locker(fLock); + + if(Phase() >= PPP_ESTABLISHMENT_PHASE) + BringHandlersUp(); +} + + +void +PPPStateMachine::DownEvent(PPPProtocol *protocol) +{ +} + + +void +PPPStateMachine::UpFailedEvent(PPPEncapsulator *encapsulator) +{ + if((encapsulator->Flags() & PPP_NOT_IMPORTANT) == 0) { + if(Interface().Mode() == PPP_CLIENT_MODE) { + // pretend we lost connection: + if(Interface().IsMultilink() && !Interface().Parent()) + for(int32 index = 0; index < Interface().CountChildren(); index++) + Interface().ChildAt(index)->StateMachine().CloseEvent(); + else if(Interface().Device()) + Interface().Device()->Down(); + else + CloseEvent(); + // just to be on the secure side ;) + } else + CloseEvent(); + } +} + + +void +PPPStateMachine::UpEvent(PPPEncapsulator *encapsulator) +{ + LockerHelper locker(fLock); + + if(Phase() >= PPP_ESTABLISHMENT_PHASE) + BringHandlersUp(); +} + + +void +PPPStateMachine::DownEvent(PPPEncapsulator *encapsulator) +{ +} + + +// 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 +PPPStateMachine::TLSNotify() +{ + LockerHelper locker(fLock); + + if(State() == PPP_STARTING_STATE) { + if(Phase() == PPP_DOWN_PHASE) + NewPhase(PPP_ESTABLISHMENT_PHASE); + // this says that the device is going up + return true; + } + + return false; +} + + +// This is called by the device to tell us that it entered termination phase. +// A Device::Up() should wait until the device went down. +// If false is returned we want to stay connected, though we called +// Device::Down(). +bool +PPPStateMachine::TLFNotify() +{ + LockerHelper locker(fLock); + + // from now on no packets may be sent to the device + NewPhase(PPP_DOWN_PHASE); + + return true; +} + + +void +PPPStateMachine::UpFailedEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_STARTING_STATE: + // TLSNotify() 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; + } + + Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_DEVICE_UP_FAILED, + NULL, 0); + + if(Interface().Parent()) + Interface().Parent()->StateMachine().UpFailedEvent(Interface()); + break; + + default: + IllegalEvent(PPP_UP_FAILED_EVENT); + } +} + + +void +PPPStateMachine::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(!Interface().Device() || !Interface().Device()->IsUp()) + return; + // it is not our device that went up... + + Interface().CalculateBaudRate(); + + switch(State()) { + case PPP_INITIAL_STATE: + if(Interface().Mode() != PPP_SERVER_MODE + || Phase() != PPP_ESTABLISHMENT_PHASE) { + // we are a client or we do not listen for an incoming + // connection, so this is an illegal event + IllegalEvent(PPP_UP_EVENT); + NewState(PPP_CLOSED_STATE); + locker.UnlockNow(); + ThisLayerFinished(); + + return; + } + + // TODO: handle server-up! (maybe already done correctly) + NewState(PPP_REQ_SENT_STATE); + InitializeRestartCount(); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + case PPP_STARTING_STATE: + // we must have called TLS() which sets establishment phase + if(Phase() != PPP_ESTABLISHMENT_PHASE) { + // there must be a BUG in the device add-on or someone is trying to + // fool us (UpEvent() is public) as we did not request the device + // to go up + IllegalEvent(PPP_UP_EVENT); + NewState(PPP_CLOSED_STATE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + } + + NewState(PPP_REQ_SENT_STATE); + InitializeRestartCount(); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + default: + IllegalEvent(PPP_UP_EVENT); + } +} + + +void +PPPStateMachine::DownEvent() +{ + LockerHelper locker(fLock); + + if(Interface().Device() && Interface().Device()->IsUp()) + return; + // it is not our device that went up... + + Interface().CalculateBaudRate(); + + // reset IdleSince + Interface().fIdleSince = 0; + + switch(State()) { + case PPP_CLOSED_STATE: + case PPP_CLOSING_STATE: + NewState(PPP_INITIAL_STATE); + break; + + case PPP_STOPPED_STATE: + // The RFC says we should reconnect, but our implementation + // will only do this if auto-redial is enabled (only clients). + NewState(PPP_STARTING_STATE); + break; + + case PPP_STOPPING_STATE: + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + case PPP_OPENED_STATE: + NewState(PPP_STARTING_STATE); + break; + + default: + IllegalEvent(PPP_DOWN_EVENT); + } + + NewPhase(PPP_DOWN_PHASE); + + DownProtocols(); + DownEncapsulators(); + + fLocalAuthenticationStatus = PPP_NOT_AUTHENTICATED; + fPeerAuthenticationStatus = PPP_NOT_AUTHENTICATED; + + // maybe we need to redial + if(State() == PPP_STARTING_STATE) { + bool needsRedial = false; + + if(fLocalAuthenticationStatus == PPP_AUTHENTICATION_FAILED + || fLocalAuthenticationStatus == PPP_AUTHENTICATING) + Interface().Report(PPP_CONNECTION_REPORT, + PPP_REPORT_LOCAL_AUTHENTICATION_FAILED, NULL, 0); + else if(fPeerAuthenticationStatus == PPP_AUTHENTICATION_FAILED + || fPeerAuthenticationStatus == PPP_AUTHENTICATING) + Interface().Report(PPP_CONNECTION_REPORT, + PPP_REPORT_PEER_AUTHENTICATION_FAILED, NULL, 0); + else { + // if we are going up and lost connection the redial attempt becomes + // a dial retry which is managed by the main thread in Interface::Up() + if(Interface().fUpThread == -1) + needsRedial = true; + + Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_CONNECTION_LOST, + NULL, 0); + } + + if(Interface().Parent()) + Interface().Parent()->StateMachine().UpFailedEvent(Interface()); + + NewState(PPP_INITIAL_STATE); + + if(Interface().DoesAutoRedial()) { + if(needsRedial) + Interface().Redial(Interface().RedialDelay()); + } else if(!Interface().DoesDialOnDemand()) + Interface().Delete(); + } else { + Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_DOWN_SUCCESSFUL, NULL, 0); + + if(!Interface().DoesDialOnDemand()) + Interface().Delete(); + } +} + + +// private events +void +PPPStateMachine::OpenEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + if(!Interface().Report(PPP_CONNECTION_REPORT, PPP_REPORT_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 index = 0; index < Interface().CountChildren(); index++) + if(Interface().ChildAt(index)->Mode() == Interface().Mode()) + Interface().ChildAt(index)->StateMachine().OpenEvent(); + } else { + locker.UnlockNow(); + ThisLayerStarted(); + } + break; + + case PPP_CLOSED_STATE: + if(Phase() == PPP_DOWN_PHASE) { + // the device is already going down + return; + } + + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + InitializeRestartCount(); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + case PPP_CLOSING_STATE: + NewState(PPP_STOPPING_STATE); + break; + + default: + ; + } +} + + +void +PPPStateMachine::CloseEvent() +{ + LockerHelper locker(fLock); + + if(Interface().IsMultilink() && !Interface().Parent()) { + NewState(PPP_INITIAL_STATE); + + if(Phase() != PPP_DOWN_PHASE) + NewPhase(PPP_TERMINATION_PHASE); + + ThisLayerDown(); + + for(int32 index = 0; index < Interface().CountChildren(); index++) + Interface().ChildAt(index)->StateMachine().CloseEvent(); + + return; + } + + switch(State()) { + case PPP_OPENED_STATE: + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_CLOSING_STATE); + NewPhase(PPP_TERMINATION_PHASE); + // indicates to handlers that we are terminating + InitializeRestartCount(); + locker.UnlockNow(); + if(State() == PPP_OPENED_STATE) + ThisLayerDown(); + SendTerminateRequest(); + break; + + case PPP_STARTING_STATE: + NewState(PPP_INITIAL_STATE); + + // TLSNotify() will know that we were faster because we + // are in PPP_INITIAL_STATE now + if(Phase() == PPP_ESTABLISHMENT_PHASE) { + // the device is already up + NewPhase(PPP_DOWN_PHASE); + // this says the following DownEvent() was not caused by + // a connection fault + locker.UnlockNow(); + ThisLayerFinished(); + } + break; + + case PPP_STOPPING_STATE: + NewState(PPP_CLOSING_STATE); + break; + + case PPP_STOPPED_STATE: + NewState(PPP_STOPPED_STATE); + break; + + default: + ; + } +} + + +// timeout (restart counters are > 0) +void +PPPStateMachine::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 +PPPStateMachine::TOBadEvent() +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_CLOSING_STATE: + NewState(PPP_CLOSED_STATE); + NewPhase(PPP_TERMINATION_PHASE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_STOPPING_STATE: + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_STOPPED_STATE); + NewPhase(PPP_TERMINATION_PHASE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + + default: + IllegalEvent(PPP_TO_BAD_EVENT); + } +} + + +// receive configure request (acceptable request) +void +PPPStateMachine::RCRGoodEvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RCR_GOOD_EVENT); + m_freem(packet); + break; + + case PPP_CLOSED_STATE: + locker.UnlockNow(); + SendTerminateAck(); + m_freem(packet); + break; + + case PPP_STOPPED_STATE: + // irc,scr,sca/8 + // XXX: should we do nothing and wait for DownEvent()? + m_freem(packet); + break; + + case PPP_REQ_SENT_STATE: + NewState(PPP_ACK_SENT_STATE); + + case PPP_ACK_SENT_STATE: + locker.UnlockNow(); + SendConfigureAck(packet); + break; + + case PPP_ACK_RCVD_STATE: + NewState(PPP_OPENED_STATE); + locker.UnlockNow(); + SendConfigureAck(packet); + ThisLayerUp(); + break; + + case PPP_OPENED_STATE: + // tld,scr,sca/8 + NewState(PPP_ACK_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + // indicates to handlers that we are reconfiguring + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + SendConfigureAck(packet); + break; + + default: + m_freem(packet); + } +} + + +// receive configure request (unacceptable request) +void +PPPStateMachine::RCRBadEvent(struct mbuf *nak, struct 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); + NewPhase(PPP_ESTABLISHMENT_PHASE); + // indicates to handlers that we are reconfiguring + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + + case PPP_ACK_SENT_STATE: + if(State() == PPP_ACK_SENT_STATE) + NewState(PPP_REQ_SENT_STATE); + // OPENED_STATE might have set this already + + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + locker.UnlockNow(); + if(nak && ntohs(mtod(nak, ppp_lcp_packet*)->length) > 3) + SendConfigureNak(nak); + else if(reject && ntohs(mtod(reject, ppp_lcp_packet*)->length) > 3) + SendConfigureNak(reject); + return; + // prevents the nak/reject from being m_freem()'d + + default: + ; + } + + if(nak) + m_freem(nak); + if(reject) + m_freem(reject); +} + + +// receive configure ack +void +PPPStateMachine::RCAEvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + if(fRequestID != mtod(packet, ppp_lcp_packet*)->id) { + // this packet is not a reply to our request + + // TODO: + // log this event + m_freem(packet); + return; + } + + // let the option handlers parse this ack + PPPConfigurePacket ack(packet); + PPPOptionHandler *handler; + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) { + handler = LCP().OptionHandlerAt(index); + if(handler->ParseAck(ack) != B_OK) { + m_freem(packet); + locker.UnlockNow(); + CloseEvent(); + return; + } + } + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RCA_EVENT); + break; + + case PPP_CLOSED_STATE: + case PPP_STOPPED_STATE: + locker.UnlockNow(); + SendTerminateAck(); + break; + + case PPP_REQ_SENT_STATE: + NewState(PPP_ACK_RCVD_STATE); + InitializeRestartCount(); + break; + + case PPP_ACK_RCVD_STATE: + NewState(PPP_REQ_SENT_STATE); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + case PPP_ACK_SENT_STATE: + NewState(PPP_OPENED_STATE); + InitializeRestartCount(); + locker.UnlockNow(); + ThisLayerUp(); + break; + + case PPP_OPENED_STATE: + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + // indicates to handlers that we are reconfiguring + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + + default: + ; + } + + m_freem(packet); +} + + +// receive configure nak/reject +void +PPPStateMachine::RCNEvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + if(fRequestID != mtod(packet, ppp_lcp_packet*)->id) { + // this packet is not a reply to our request + + // TODO: + // log this event + m_freem(packet); + return; + } + + // let the option handlers parse this nak/reject + PPPConfigurePacket nak_reject(packet); + PPPOptionHandler *handler; + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) { + handler = LCP().OptionHandlerAt(index); + + if(nak_reject.Code() == PPP_CONFIGURE_NAK) { + if(handler->ParseNak(nak_reject) != B_OK) { + m_freem(packet); + locker.UnlockNow(); + CloseEvent(); + return; + } + } else if(nak_reject.Code() == PPP_CONFIGURE_REJECT) { + if(handler->ParseReject(nak_reject) != B_OK) { + m_freem(packet); + locker.UnlockNow(); + CloseEvent(); + return; + } + } + } + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RCN_EVENT); + break; + + case PPP_CLOSED_STATE: + case PPP_STOPPED_STATE: + locker.UnlockNow(); + SendTerminateAck(); + break; + + case PPP_REQ_SENT_STATE: + case PPP_ACK_SENT_STATE: + InitializeRestartCount(); + + case PPP_ACK_RCVD_STATE: + if(State() == PPP_ACK_RCVD_STATE) + NewState(PPP_REQ_SENT_STATE); + locker.UnlockNow(); + SendConfigureRequest(); + break; + + case PPP_OPENED_STATE: + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + // indicates to handlers that we are reconfiguring + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + + default: + ; + } + + m_freem(packet); +} + + +// receive terminate request +void +PPPStateMachine::RTREvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + // we should not use the same ID as the peer + if(fID == mtod(packet, ppp_lcp_packet*)->id) + fID -= 128; + + fLocalAuthenticationStatus = PPP_NOT_AUTHENTICATED; + fPeerAuthenticationStatus = PPP_NOT_AUTHENTICATED; + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RTR_EVENT); + m_freem(packet); + break; + + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_TERMINATION_PHASE); + // indicates to handlers that we are terminating + locker.UnlockNow(); + SendTerminateAck(packet); + break; + + case PPP_OPENED_STATE: + NewState(PPP_STOPPING_STATE); + NewPhase(PPP_TERMINATION_PHASE); + // indicates to handlers that we are terminating + ZeroRestartCount(); + locker.UnlockNow(); + ThisLayerDown(); + SendTerminateAck(packet); + break; + + default: + NewPhase(PPP_TERMINATION_PHASE); + // indicates to handlers that we are terminating + locker.UnlockNow(); + SendTerminateAck(packet); + } +} + + +// receive terminate ack +void +PPPStateMachine::RTAEvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + if(fTerminateID != mtod(packet, ppp_lcp_packet*)->id) { + // this packet is not a reply to our request + + // TODO: + // log this event + m_freem(packet); + return; + } + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RTA_EVENT); + break; + + case PPP_CLOSING_STATE: + NewState(PPP_CLOSED_STATE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_STOPPING_STATE: + NewState(PPP_STOPPED_STATE); + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_ACK_RCVD_STATE: + NewState(PPP_REQ_SENT_STATE); + break; + + case PPP_OPENED_STATE: + NewState(PPP_REQ_SENT_STATE); + NewPhase(PPP_ESTABLISHMENT_PHASE); + // indicates to handlers that we are reconfiguring + locker.UnlockNow(); + ThisLayerDown(); + SendConfigureRequest(); + break; + + default: + ; + } + + m_freem(packet); +} + + +// receive unknown code +void +PPPStateMachine::RUCEvent(struct mbuf *packet, uint16 protocol, + uint8 code = PPP_PROTOCOL_REJECT) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RUC_EVENT); + m_freem(packet); + break; + + default: + locker.UnlockNow(); + SendCodeReject(packet, protocol, code); + } +} + + +// receive code/protocol reject (acceptable such as IPX reject) +void +PPPStateMachine::RXJGoodEvent(struct mbuf *packet) +{ + // This method does not m_freem(packet) because the acceptable rejects are + // also passed to the parent. RXJEvent() will m_freem(packet) when needed. + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RXJ_GOOD_EVENT); + break; + + case PPP_ACK_RCVD_STATE: + NewState(PPP_REQ_SENT_STATE); + break; + + default: + ; + } +} + + +// receive code/protocol reject (catastrophic such as LCP reject) +void +PPPStateMachine::RXJBadEvent(struct mbuf *packet) +{ + LockerHelper locker(fLock); + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RXJ_BAD_EVENT); + break; + + case PPP_CLOSING_STATE: + NewState(PPP_CLOSED_STATE); + + case PPP_CLOSED_STATE: + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + NewState(PPP_STOPPED_STATE); + + case PPP_STOPPING_STATE: + NewPhase(PPP_TERMINATION_PHASE); + + case PPP_STOPPED_STATE: + locker.UnlockNow(); + ThisLayerFinished(); + break; + + case PPP_OPENED_STATE: + NewState(PPP_STOPPING_STATE); + NewPhase(PPP_TERMINATION_PHASE); + // indicates to handlers that we are terminating + InitializeRestartCount(); + locker.UnlockNow(); + ThisLayerDown(); + SendTerminateRequest(); + break; + } + + m_freem(packet); +} + + +// receive echo request/reply, discard request +void +PPPStateMachine::RXREvent(struct mbuf *packet) +{ + ppp_lcp_packet *echo = mtod(packet, ppp_lcp_packet*); + + if(echo->code == PPP_ECHO_REPLY && echo->id != fEchoID) { + // TODO: + // log that we got a reply, but no request was sent + } + + switch(State()) { + case PPP_INITIAL_STATE: + case PPP_STARTING_STATE: + IllegalEvent(PPP_RXR_EVENT); + break; + + case PPP_OPENED_STATE: + if(echo->code == PPP_ECHO_REQUEST) + SendEchoReply(packet); + return; + // this prevents the packet from being freed + + default: + ; + } + + m_freem(packet); +} + + +// general events (for Good/Bad events) +void +PPPStateMachine::TimerEvent() +{ + LockerHelper locker(fLock); + if(fNextTimeout == 0 || fNextTimeout > system_time()) + return; + fNextTimeout = 0; + locker.UnlockNow(); + + switch(State()) { + case PPP_CLOSING_STATE: + case PPP_STOPPING_STATE: + if(fTerminateCounter <= 0) + TOBadEvent(); + else + TOGoodEvent(); + break; + + case PPP_REQ_SENT_STATE: + case PPP_ACK_RCVD_STATE: + case PPP_ACK_SENT_STATE: + if(fRequestCounter <= 0) + TOBadEvent(); + else + TOGoodEvent(); + break; + + default: + ; + } + +} + + +// ReceiveConfigureRequest +// Here we get a configure-request packet from LCP and aks all OptionHandlers +// if its values are acceptable. From here we call our Good/Bad counterparts. +void +PPPStateMachine::RCREvent(struct mbuf *packet) +{ + PPPConfigurePacket request(packet), nak(PPP_CONFIGURE_NAK), + reject(PPP_CONFIGURE_REJECT); + + // we should not use the same id as the peer + if(fID == mtod(packet, ppp_lcp_packet*)->id) + fID -= 128; + + nak.SetID(request.ID()); + reject.SetID(request.ID()); + + // each handler should add unacceptable values for each item + status_t result; + // the return value of ParseRequest() + PPPOptionHandler *handler; + for(int32 index = 0; index < request.CountItems(); index++) { + handler = LCP().OptionHandlerFor(request.ItemAt(index)->type); + + if(!handler || !handler->IsEnabled()) { + // unhandled items should be added to the reject + reject.AddItem(request.ItemAt(index)); + continue; + } + + result = handler->ParseRequest(request, index, nak, reject); + + if(result == PPP_UNHANDLED) { + // unhandled items should be added to the reject + reject.AddItem(request.ItemAt(index)); + continue; + } else if(result != B_OK) { + // the request contains a value that has been sent more than + // once or the value is corrupted + m_freem(packet); + CloseEvent(); + return; + } + } + + // Additional values may be appended. + // If we sent too many naks we should not append additional values. + if(fNakCounter > 0) { + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) { + handler = LCP().OptionHandlerAt(index); + if(handler && handler->IsEnabled()) { + result = handler->ParseRequest(request, request.CountItems(), + nak, reject); + + if(result != B_OK) { + // the request contains a value that has been sent more than + // once or the value is corrupted + m_freem(packet); + CloseEvent(); + return; + } + } + } + } + + if(nak.CountItems() > 0) + RCRBadEvent(nak.ToMbuf(LCP().AdditionalOverhead()), NULL); + else if(reject.CountItems() > 0) + RCRBadEvent(NULL, reject.ToMbuf(LCP().AdditionalOverhead())); + else + RCRGoodEvent(packet); +} + + +// ReceiveCodeReject +// LCP received a code/protocol-reject packet and we look if it is acceptable. +// From here we call our Good/Bad counterparts. +void +PPPStateMachine::RXJEvent(struct mbuf *packet) +{ + ppp_lcp_packet *reject = mtod(packet, ppp_lcp_packet*); + + if(reject->code == PPP_CODE_REJECT) { + uint8 rejectedCode = reject->data[0]; + + // test if the rejected code belongs to the minimum LCP requirements + if(rejectedCode >= PPP_MIN_LCP_CODE && rejectedCode <= PPP_MAX_LCP_CODE) { + if(Interface().IsMultilink() && !Interface().Parent()) { + // Main interfaces do not have states between STARTING and OPENED. + // An RXJBadEvent() would enter one of those states which is bad. + m_freem(packet); + CloseEvent(); + } else + RXJBadEvent(packet); + + return; + } + + // find the LCP extension and disable it + PPPLCPExtension *extension; + for(int32 index = 0; index < LCP().CountLCPExtensions(); index++) { + extension = LCP().LCPExtensionAt(index); + + if(extension->Code() == rejectedCode) + extension->SetEnabled(false); + } + + m_freem(packet); + } else if(reject->code == PPP_PROTOCOL_REJECT) { + // disable all handlers for rejected protocol type + uint16 rejected = *((uint16*) reject->data); + // rejected protocol number + + if(rejected == PPP_LCP_PROTOCOL) { + // LCP must not be rejected! + RXJBadEvent(packet); + return; + } + + // disable protocols and encapsulators with the rejected protocl number + int32 index; + PPPProtocol *protocol_handler; + PPPEncapsulator *encapsulator_handler = Interface().FirstEncapsulator(); + + for(index = 0; index < Interface().CountProtocols(); index++) { + protocol_handler = Interface().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); + // this event handler does not m_freem(packet)!!! + + // notify parent, too + if(Interface().Parent()) + Interface().Parent()->StateMachine().RXJEvent(packet); + else + m_freem(packet); + } +} + + +// actions (all private) +void +PPPStateMachine::IllegalEvent(ppp_event event) +{ + // TODO: + // update error statistics +} + + +void +PPPStateMachine::ThisLayerUp() +{ + LockerHelper locker(fLock); + + // We begin with authentication phase and wait until each phase is done. + // We stop when we reach established phase. + + // Do not forget to check if we are going down. + if(Phase() != PPP_ESTABLISHMENT_PHASE) + return; + + NewPhase(PPP_AUTHENTICATION_PHASE); + + locker.UnlockNow(); + + BringHandlersUp(); +} + + +void +PPPStateMachine::ThisLayerDown() +{ + // PPPProtocol/Encapsulator::Down() should block if needed. + DownProtocols(); + DownEncapsulators(); +} + + +void +PPPStateMachine::ThisLayerStarted() +{ + if(Interface().Device()) + Interface().Device()->Up(); +} + + +void +PPPStateMachine::ThisLayerFinished() +{ + if(Interface().Device()) + Interface().Device()->Down(); +} + + +void +PPPStateMachine::InitializeRestartCount() +{ + fRequestCounter = fMaxRequest; + fTerminateCounter = fMaxTerminate; + fNakCounter = fMaxNak; + + LockerHelper locker(fLock); + fNextTimeout = system_time() + PPP_STATE_MACHINE_TIMEOUT; +} + + +void +PPPStateMachine::ZeroRestartCount() +{ + fRequestCounter = 0; + fTerminateCounter = 0; + fNakCounter = 0; + + LockerHelper locker(fLock); + fNextTimeout = system_time() + PPP_STATE_MACHINE_TIMEOUT; +} + + +bool +PPPStateMachine::SendConfigureRequest() +{ + --fRequestCounter; + + PPPConfigurePacket request(PPP_CONFIGURE_REQUEST); + request.SetID(NextID()); + fRequestID = request.ID(); + + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) { + // add all items + if(LCP().OptionHandlerAt(index)->AddToRequest(request) != B_OK) { + CloseEvent(); + return false; + } + } + + return LCP().Send(request.ToMbuf(LCP().AdditionalOverhead())) == B_OK; +} + + +bool +PPPStateMachine::SendConfigureAck(struct mbuf *packet) +{ + if(!packet) + return false; + + mtod(packet, ppp_lcp_packet*)->code = PPP_CONFIGURE_ACK; + PPPConfigurePacket ack(packet); + + // notify all option handlers that we are sending an ack for each value + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) { + if(LCP().OptionHandlerAt(index)->SendingAck(ack) != B_OK) { + m_freem(packet); + CloseEvent(); + return false; + } + } + + return LCP().Send(packet) == B_OK; +} + + +bool +PPPStateMachine::SendConfigureNak(struct mbuf *packet) +{ + if(!packet) + return false; + + ppp_lcp_packet *nak = mtod(packet, ppp_lcp_packet*); + if(nak->code == PPP_CONFIGURE_NAK) { + if(fNakCounter == 0) { + // We sent enough naks. Let's try a reject. + nak->code = PPP_CONFIGURE_REJECT; + } else + --fNakCounter; + } + + return LCP().Send(packet) == B_OK; +} + + +bool +PPPStateMachine::SendTerminateRequest() +{ + struct mbuf *m = m_gethdr(MT_DATA); + if(!m) + return false; + + --fTerminateCounter; + + m->m_len = 4; + + // reserve some space for other protocols + m->m_data += LCP().AdditionalOverhead(); + + ppp_lcp_packet *request = mtod(m, ppp_lcp_packet*); + request->code = PPP_TERMINATE_REQUEST; + request->id = fTerminateID = NextID(); + request->length = htons(4); + + return LCP().Send(m) == B_OK; +} + + +bool +PPPStateMachine::SendTerminateAck(struct mbuf *request = NULL) +{ + struct mbuf *reply = request; + + ppp_lcp_packet *ack; + + if(!reply) { + reply = m_gethdr(MT_DATA); + if(!reply) + return false; + + reply->m_data += LCP().AdditionalOverhead(); + reply->m_len = 4; + + ack = mtod(reply, ppp_lcp_packet*); + ack->id = NextID(); + } + + ack = mtod(reply, ppp_lcp_packet*); + ack->code = PPP_TERMINATE_ACK; + ack->length = htons(4); + + return LCP().Send(reply) == B_OK; +} + + +bool +PPPStateMachine::SendCodeReject(struct mbuf *packet, uint16 protocol, uint8 code) +{ + if(!packet) + return false; + + int32 length; + // additional space needed for this reject + if(code == PPP_PROTOCOL_REJECT) + length = 6; + else + length = 4; + + M_PREPEND(packet, length); + // add some space for the header + + int32 adjust = 0; + // adjust packet size by this value if packet is too big + if(packet->m_flags & M_PKTHDR) { + if((uint32) packet->m_pkthdr.len > Interface().MRU()) + adjust = Interface().MRU() - packet->m_pkthdr.len; + } else if(packet->m_len > Interface().MRU()) + adjust = Interface().MRU() - packet->m_len; + + if(adjust != 0) + m_adj(packet, adjust); + + ppp_lcp_packet *reject = mtod(packet, ppp_lcp_packet*); + reject->code = code; + reject->id = NextID(); + if(packet->m_flags & M_PKTHDR) + reject->length = htons(packet->m_pkthdr.len); + else + reject->length = htons(packet->m_len); + + protocol = htons(protocol); + if(code == PPP_PROTOCOL_REJECT) + memcpy(&reject->data, &protocol, sizeof(protocol)); + + return LCP().Send(packet) == B_OK; +} + + +bool +PPPStateMachine::SendEchoReply(struct mbuf *request) +{ + if(!request) + return false; + + ppp_lcp_packet *reply = mtod(request, ppp_lcp_packet*); + reply->code = PPP_ECHO_REPLY; + // the request becomes a reply + + if(request->m_flags & M_PKTHDR) + request->m_pkthdr.len = 8; + + request->m_len = 8; + + memcpy(reply->data, &fMagicNumber, sizeof(fMagicNumber)); + + return LCP().Send(request) == B_OK; +} + + +// methods for bringing protocols/encapsulators up +void +PPPStateMachine::BringHandlersUp() +{ + // use a simple check for phase changes (e.g., caused by CloseEvent()) + while(Phase() <= PPP_ESTABLISHED_PHASE && Phase() >= PPP_AUTHENTICATION_PHASE) { + if(BringPhaseUp() > 0) + break; + + LockerHelper locker(fLock); + + if(Phase() < PPP_AUTHENTICATION_PHASE) + return; + // phase was changed by another event + else if(Phase() == PPP_ESTABLISHED_PHASE) { + if(Interface().Parent()) + Interface().Parent()->StateMachine().UpEvent(Interface()); + } else + NewPhase((ppp_phase) (Phase() + 1)); + } +} + + +// this returns the number of handlers waiting to go up +uint32 +PPPStateMachine::BringPhaseUp() +{ + LockerHelper locker(fLock); + + // check for phase change + if(Phase() < PPP_AUTHENTICATION_PHASE) + return 0; + + uint32 count = 0; + PPPProtocol *protocol_handler; + PPPEncapsulator *encapsulator_handler = Interface().FirstEncapsulator(); + + for(int32 index = 0; index < Interface().CountProtocols(); index++) { + protocol_handler = Interface().ProtocolAt(index); + if(protocol_handler && protocol_handler->IsEnabled() + && protocol_handler->Phase() == Phase()) { + if(protocol_handler->IsUpRequested()) { + ++count; + protocol_handler->Up(); + } else if(protocol_handler->IsGoingUp()) + ++count; + } + } + + for(; encapsulator_handler; + encapsulator_handler = encapsulator_handler->Next()) { + if(encapsulator_handler && encapsulator_handler->IsEnabled() + && encapsulator_handler->Phase() == Phase()) { + if(encapsulator_handler->IsUpRequested()) { + ++count; + encapsulator_handler->Up(); + } else if(encapsulator_handler->IsGoingUp()) + ++count; + } + } + + return count; +} + + +void +PPPStateMachine::DownProtocols() +{ + for(int32 index = 0; index < Interface().CountProtocols(); index++) + if(Interface().ProtocolAt(index)->IsEnabled()) + Interface().ProtocolAt(index)->Down(); +} + + +void +PPPStateMachine::DownEncapsulators() +{ + PPPEncapsulator *encapsulator_handler = Interface().FirstEncapsulator(); + + for(; encapsulator_handler; + encapsulator_handler = encapsulator_handler->Next()) + if(encapsulator_handler->IsEnabled()) + encapsulator_handler->Down(); +} + + +void +PPPStateMachine::ResetLCPHandlers() +{ + for(int32 index = 0; index < LCP().CountOptionHandlers(); index++) + LCP().OptionHandlerAt(index)->Reset(); + + for(int32 index = 0; index < LCP().CountLCPExtensions(); index++) + LCP().LCPExtensionAt(index)->Reset(); +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPUtils.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPUtils.cpp new file mode 100644 index 0000000000..c8d9e0c0e4 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/KPPPUtils.cpp @@ -0,0 +1,48 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include + +#include + + +// These are very simple send/receive_data functions with a timeout +// and there is a race condition beween has_data() and send/receive_data(). +// Timeouts in ms. +status_t +send_data_with_timeout(thread_id thread, int32 code, void *buffer, + size_t buffer_size, uint32 timeout) +{ + for(uint32 tries = 0; tries < timeout; tries++) { + if(has_data(thread)) + snooze(1000); + } + + if(!has_data(thread)) + return send_data(thread, code, buffer, buffer_size); + else + return B_TIMED_OUT; +} + + +status_t +receive_data_with_timeout(thread_id *sender, int32 *code, void *buffer, + size_t buffer_size, uint32 timeout) +{ + for(uint32 tries = 0; tries < timeout; tries++) { + if(!has_data(find_thread(NULL))) { + snooze(1000); + continue; + } + } + + if(has_data(find_thread(NULL))) { + *code = receive_data(sender, buffer, buffer_size); + return B_OK; + } else + return B_TIMED_OUT; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.cpp new file mode 100644 index 0000000000..bf6517aef4 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.cpp @@ -0,0 +1,283 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include "_KPPPAuthenticationHandler.h" + +#include +#include + +#include + +#define AUTHENTICATION_TYPE 0x3 +#define AUTHENTICATOR_TYPE_STRING "Authenticator" + +typedef struct authentication_item { + uint8 type; + uint8 length; + uint16 protocol; +} authentication_item; + + +_PPPAuthenticationHandler::_PPPAuthenticationHandler(PPPInterface& interface) + : PPPOptionHandler("Authentication Handler", AUTHENTICATION_TYPE, interface, NULL), + fLocalIndex(-1), + fPeerIndex(-1), + fSuggestedLocalIndex(-1), + fSuggestedPeerIndex(-1), + fPeerAuthenticatorRejected(false) +{ +} + + +int32 +_PPPAuthenticationHandler::NextAuthenticator(int32 start, ppp_side side) +{ + // find the next authenticator for side, beginning at start + + PPPProtocol *protocol; + for(int32 index = start; index < Interface().CountProtocols(); index++) { + protocol = Interface().ProtocolAt(index); + if(protocol && !strcasecmp(protocol->Type(), AUTHENTICATOR_TYPE_STRING) + && protocol->OptionHandler() + && protocol->Side() == side) + return index; + } + + return -1; +} + + +status_t +_PPPAuthenticationHandler::AddToRequest(PPPConfigurePacket& request) +{ + // AddToRequest(): Check if peer must authenticate itself and + // add an authentication request if needed. This request is added + // by the authenticator's OptionHandler. + + int32 index; + PPPProtocol *protocol; + if(fPeerIndex != -1 && (protocol = Interface().ProtocolAt(fPeerIndex))) + protocol->SetEnabled(false); + if(fSuggestedPeerIndex != -1 + && (protocol = Interface().ProtocolAt(fSuggestedPeerIndex))) + protocol->SetEnabled(false); + + if(fPeerAuthenticatorRejected) { + if(fSuggestedPeerIndex == -1) { + // This happens when the protocol is rejected, but no alternative + // protocol is supplied to us or the suggested protocol is not supported. + // We can use this chance to increase fPeerIndex to the next authenticator. + index = NextAuthenticator(fPeerIndex + 1, PPP_PEER_SIDE); + } else + index = fSuggestedPeerIndex; + + fPeerAuthenticatorRejected = false; + } else { + if(fPeerIndex == -1) { + // there is no authenticator selected, so find one for us + index = NextAuthenticator(fPeerIndex + 1, PPP_PEER_SIDE); + } else + index = fPeerIndex; + } + + // check if all authenticators were rejected or if no authentication needed + if(index == -1) { + if(fPeerIndex == -1) + return B_OK; + // no peer authentication needed + else + return B_ERROR; + // all authenticators were denied + } + + protocol = Interface().ProtocolAt(index); + if(!protocol || !protocol->OptionHandler()) + return B_ERROR; + + fPeerIndex = index; + // this could omit some authenticators when we get a suggestion, but that is + // no problem because the suggested authenticator will be accepted (hopefully) + + protocol->SetEnabled(true); + return protocol->OptionHandler()->AddToRequest(request); + // let protocol add its request +} + + +status_t +_PPPAuthenticationHandler::ParseNak(const PPPConfigurePacket& nak) +{ + // The authenticator's OptionHandler is not notified. + + authentication_item *item = + (authentication_item*) nak.ItemWithType(AUTHENTICATION_TYPE); + + if(!item) + return B_OK; + // the request was not rejected + if(item->length < 4) + return B_ERROR; + + PPPProtocol *protocol; + if(fSuggestedPeerIndex != -1 + && (protocol = Interface().ProtocolAt(fSuggestedPeerIndex))) { + protocol->SetEnabled(false); + // if no alternative protocol is supplied we will choose a new one in + // AddToRequest() + if(ntohs(item->protocol) == protocol->Protocol()) { + fSuggestedPeerIndex = -1; + return B_OK; + } + } + + fPeerAuthenticatorRejected = true; + int32 index = 0; + if((protocol = Interface().ProtocolFor(ntohs(item->protocol), &index)) + && !strcasecmp(protocol->Type(), AUTHENTICATOR_TYPE_STRING) + && protocol->OptionHandler()) + fSuggestedPeerIndex = index; + else + fSuggestedPeerIndex = -1; + + return B_OK; +} + + +status_t +_PPPAuthenticationHandler::ParseReject(const PPPConfigurePacket& reject) +{ + // an authentication request must not be rejected! + if(reject.ItemWithType(AUTHENTICATION_TYPE)) + return B_ERROR; + + return B_OK; +} + + +status_t +_PPPAuthenticationHandler::ParseAck(const PPPConfigurePacket& ack) +{ + authentication_item *item = + (authentication_item*) ack.ItemWithType(AUTHENTICATION_TYPE); + + PPPProtocol *protocol = Interface().ProtocolAt(fPeerIndex); + if(fPeerIndex != -1 && !protocol) + return B_ERROR; + // could not find the protocol + + if(!item) { + if(fPeerIndex != -1) + return B_ERROR; + // the ack does not contain our request + } else if(fPeerIndex == -1 || ntohs(item->protocol) != protocol->Protocol()) + return B_ERROR; + // this item was never requested + + return protocol->OptionHandler()->ParseAck(ack); + // this should enable the authenticator +} + + +status_t +_PPPAuthenticationHandler::ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject) +{ + PPPProtocol *protocol; + if(fLocalIndex != -1 && (protocol = Interface().ProtocolAt(fLocalIndex))) + protocol->SetEnabled(false); + + authentication_item *item = (authentication_item*) request.ItemAt(index); + if(!item) + return B_OK; + // no authentication requested by peer + + // try to find the requested protocol and select it by saving it in fLocalIndex + fLocalIndex = 0; + protocol = Interface().ProtocolFor(ntohs(item->protocol), &fLocalIndex); + if(protocol && !strcasecmp(protocol->Type(), AUTHENTICATOR_TYPE_STRING) + && protocol->OptionHandler()) + return protocol->OptionHandler()->ParseRequest(request, index, nak, reject); + + // suggest another authentication protocol + int32 nextIndex = NextAuthenticator(fSuggestedLocalIndex + 1, PPP_LOCAL_SIDE); + + if(nextIndex == -1) { + if(fSuggestedLocalIndex == -1) { + // reject the complete authentication option + reject.AddItem((ppp_configure_item*) item); + return B_OK; + } else + nextIndex = fSuggestedLocalIndex; + // try the old one again as it was not rejected until now + } + + protocol = Interface().ProtocolAt(nextIndex); + if(!protocol) + return B_ERROR; + + fSuggestedLocalIndex = nextIndex; + fLocalIndex = -1; + // no authenticator selected + + // nak this authenticator and suggest an alternative + authentication_item suggestion; + suggestion.type = AUTHENTICATION_TYPE; + suggestion.length = 4; + suggestion.protocol = htons(protocol->Protocol()); + return nak.AddItem((ppp_configure_item*) &suggestion) ? B_OK : B_ERROR; +} + + +status_t +_PPPAuthenticationHandler::SendingAck(const PPPConfigurePacket& ack) +{ + // do not insist on authenticating our side of the link ;) + + authentication_item *item = + (authentication_item*) ack.ItemWithType(AUTHENTICATION_TYPE); + + if(!item) + return B_OK; + // no authentication needed + + fSuggestedLocalIndex = -1; + + if(fLocalIndex == -1) + return B_ERROR; + // no authenticator selected (our suggestions must be requested, too) + + PPPProtocol *protocol = Interface().ProtocolAt(fLocalIndex); + if(!protocol) + return B_ERROR; + + protocol->SetEnabled(true); + return protocol->OptionHandler()->SendingAck(ack); + // this should enable the authenticator +} + + +void +_PPPAuthenticationHandler::Reset() +{ + PPPProtocol *protocol = NULL; + if(fLocalIndex != -1 && (protocol = Interface().ProtocolAt(fLocalIndex))) { + protocol->SetEnabled(false); + protocol->OptionHandler()->Reset(); + } + if(fPeerIndex != -1 && (protocol = Interface().ProtocolAt(fPeerIndex))) { + protocol->SetEnabled(false); + protocol->OptionHandler()->Reset(); + } + if(fSuggestedPeerIndex != -1 + && (protocol = Interface().ProtocolAt(fSuggestedPeerIndex))) { + protocol->SetEnabled(false); + protocol->OptionHandler()->Reset(); + } + + fLocalIndex = fPeerIndex = fSuggestedLocalIndex = fSuggestedPeerIndex = -1; + fPeerAuthenticatorRejected = false; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.h new file mode 100644 index 0000000000..c9264f307e --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPAuthenticationHandler.h @@ -0,0 +1,37 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef __K_PPP_AUTHENTICATION_HANDLER__H +#define __K_PPP_AUTHENTICATION_HANDLER__H + +#include + + +class _PPPAuthenticationHandler : public PPPOptionHandler { + public: + _PPPAuthenticationHandler(PPPInterface& interface); + + int32 NextAuthenticator(int32 start, ppp_side side); + + virtual status_t AddToRequest(PPPConfigurePacket& request); + virtual status_t ParseNak(const PPPConfigurePacket& nak); + virtual status_t ParseReject(const PPPConfigurePacket& reject); + virtual status_t ParseAck(const PPPConfigurePacket& ack); + + virtual status_t ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject); + virtual status_t SendingAck(const PPPConfigurePacket& ack); + + virtual void Reset(); + + private: + int32 fLocalIndex, fPeerIndex, fSuggestedLocalIndex, fSuggestedPeerIndex; + bool fPeerAuthenticatorRejected; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.cpp new file mode 100644 index 0000000000..29cefdb6f9 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.cpp @@ -0,0 +1,140 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include "_KPPPMRUHandler.h" + +#include +#include + +#include + +#define MRU_TYPE 0x1 + +typedef struct mru_item { + uint8 type; + uint8 length; + uint16 MRU; +}; + +status_t ParseRequestedItem(mru_item *item, PPPInterface& interface); + + +_PPPMRUHandler::_PPPMRUHandler(PPPInterface& interface) + : PPPOptionHandler("MRU Handler", MRU_TYPE, interface, NULL) +{ + Reset(); +} + + +status_t +_PPPMRUHandler::AddToRequest(PPPConfigurePacket& request) +{ + if(!Interface().Device() || Interface().MRU() == 1500) + return B_OK; + + // add MRU request + mru_item item; + item.type = MRU_TYPE; + item.length = 4; + item.MRU = htons(fLocalMRU); + return request.AddItem((ppp_configure_item*) &item) ? B_OK : B_ERROR; +} + + +status_t +_PPPMRUHandler::ParseNak(const PPPConfigurePacket& nak) +{ + mru_item *item = (mru_item*) nak.ItemWithType(MRU_TYPE); + if(!item || item->length != 4) + return B_OK; + + uint16 MRU = ntohs(item->MRU); + if(MRU < fLocalMRU) + fLocalMRU = MRU; + + return B_OK; +} + + +status_t +_PPPMRUHandler::ParseReject(const PPPConfigurePacket& reject) +{ + if(reject.ItemWithType(MRU_TYPE)) + return B_ERROR; + + return B_OK; +} + + +status_t +_PPPMRUHandler::ParseAck(const PPPConfigurePacket& ack) +{ + uint16 MRU = 1500; + mru_item *item = (mru_item*) ack.ItemWithType(MRU_TYPE); + + if(item) + MRU = ntohs(item->MRU); + + if(MRU < Interface().MRU()) + fLocalMRU = MRU; + + return B_OK; +} + + +status_t +_PPPMRUHandler::ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject) +{ + if(index == reject.CountItems()) + return B_OK; + + return ParseRequestedItem((mru_item*) request.ItemAt(index), Interface()); + + return B_OK; +} + + +status_t +_PPPMRUHandler::SendingAck(const PPPConfigurePacket& ack) +{ + return ParseRequestedItem((mru_item*) ack.ItemWithType(MRU_TYPE), Interface()); +} + + +// this function contains code shared by ParseRequest and SendingAck +status_t +ParseRequestedItem(mru_item *item, PPPInterface& interface) +{ + uint16 MRU = 1500; + + if(item) { + if(item->length != 4) + return B_ERROR; + // the request had a corrupted item + + MRU = ntohs(item->MRU); + } + + if(MRU < interface.MRU()) + interface.SetMRU(MRU); + + return B_OK; +} + + +void +_PPPMRUHandler::Reset() +{ + if(Interface().Device()) { + fLocalMRU = Interface().Device()->MTU() - 2; + Interface().SetMRU(fLocalMRU); + } else { + Interface().SetMRU(1500); + fLocalMRU = 1500; + } +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.h new file mode 100644 index 0000000000..4418eb39d3 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPMRUHandler.h @@ -0,0 +1,34 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef __K_PPP_MRU_HANDLER__H +#define __K_PPP_MRU_HANDLER__H + +#include + + +class _PPPMRUHandler : public PPPOptionHandler { + public: + _PPPMRUHandler(PPPInterface& interface); + + virtual status_t AddToRequest(PPPConfigurePacket& request); + virtual status_t ParseNak(const PPPConfigurePacket& nak); + virtual status_t ParseReject(const PPPConfigurePacket& reject); + virtual status_t ParseAck(const PPPConfigurePacket& ack); + + virtual status_t ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject); + virtual status_t SendingAck(const PPPConfigurePacket& ack); + + virtual void Reset(); + + private: + uint16 fLocalMRU; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.cpp new file mode 100644 index 0000000000..f624f47301 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.cpp @@ -0,0 +1,120 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include "_KPPPPFCHandler.h" + +#include + +#define PFC_TYPE 0x7 + + +_PPPPFCHandler::_PPPPFCHandler(ppp_pfc_state& localPFCState, + ppp_pfc_state& peerPFCState, PPPInterface& interface) + : PPPOptionHandler("PFC Handler", PFC_TYPE, interface, NULL), + fLocalPFCState(localPFCState), + fPeerPFCState(peerPFCState) +{ +} + + +status_t +_PPPPFCHandler::AddToRequest(PPPConfigurePacket& request) +{ + // is PFC not requested or was it rejected? + if(fLocalPFCState & PPP_PFC_REJECTED + || (Interface().PFCOptions() & PPP_REQUEST_PFC) == 0) + return B_OK; + + // add PFC request + ppp_configure_item item; + item.type = PFC_TYPE; + item.length = 2; + return request.AddItem(&item) ? B_OK : B_ERROR; +} + + +status_t +_PPPPFCHandler::ParseNak(const PPPConfigurePacket& nak) +{ + // naks do not contain PFC items + if(nak.ItemWithType(PFC_TYPE)) + return B_ERROR; + + return B_OK; +} + + +status_t +_PPPPFCHandler::ParseReject(const PPPConfigurePacket& reject) +{ + if(reject.ItemWithType(PFC_TYPE)) { + fLocalPFCState = PPP_PFC_REJECTED; + + if(Interface().PFCOptions() & PPP_FORCE_PFC_REQUEST) + return B_ERROR; + } + + return B_OK; +} + + +status_t +_PPPPFCHandler::ParseAck(const PPPConfigurePacket& ack) +{ + if(ack.ItemWithType(PFC_TYPE)) + fLocalPFCState = PPP_PFC_ACCEPTED; + else { + fLocalPFCState = PPP_PFC_DISABLED; + + if(Interface().PFCOptions() & PPP_FORCE_PFC_REQUEST) + return B_ERROR; + } + + return B_OK; +} + + +status_t +_PPPPFCHandler::ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject) +{ + if(!request.ItemWithType(PFC_TYPE)) + return B_OK; + + if((Interface().PFCOptions() & PPP_ALLOW_PFC) == 0) { + ppp_configure_item item; + item.type = PFC_TYPE; + item.length = 2; + return reject.AddItem(&item) ? B_OK : B_ERROR; + } + + return B_OK; +} + + +status_t +_PPPPFCHandler::SendingAck(const PPPConfigurePacket& ack) +{ + ppp_configure_item *item = ack.ItemWithType(PFC_TYPE); + + if(item && (Interface().PFCOptions() & PPP_ALLOW_PFC) == 0) + return B_ERROR; + + if(item) + fPeerPFCState = PPP_PFC_ACCEPTED; + else + fPeerPFCState = PPP_PFC_DISABLED; + + return B_OK; +} + + +void +_PPPPFCHandler::Reset() +{ + fLocalPFCState = fPeerPFCState = PPP_PFC_DISABLED; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.h new file mode 100644 index 0000000000..c542ac3f2e --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/_KPPPPFCHandler.h @@ -0,0 +1,35 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef __K_PPP_PFC_HANDLER__H +#define __K_PPP_PFC_HANDLER__H + +#include + + +class _PPPPFCHandler : public PPPOptionHandler { + public: + _PPPPFCHandler(ppp_pfc_state& localPFCState, ppp_pfc_state& peerPFCState, + PPPInterface& interface); + + virtual status_t AddToRequest(PPPConfigurePacket& request); + virtual status_t ParseNak(const PPPConfigurePacket& nak); + virtual status_t ParseReject(const PPPConfigurePacket& reject); + virtual status_t ParseAck(const PPPConfigurePacket& ack); + + virtual status_t ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject); + virtual status_t SendingAck(const PPPConfigurePacket& ack); + + virtual void Reset(); + + private: + ppp_pfc_state &fLocalPFCState, &fPeerPFCState; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPP.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPP.h new file mode 100644 index 0000000000..2b08ec19fa --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPP.h @@ -0,0 +1,22 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP__H +#define _K_PPP__H + +#include +#include + // includes KPPPInterface.h, KPPPLCP.h, + // KPPPStateMachine.h and KPPPProtocol.h +#include +#include +#include + +#include + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPConfigurePacket.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPConfigurePacket.h new file mode 100644 index 0000000000..3c0111729c --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPConfigurePacket.h @@ -0,0 +1,59 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_CONFIGURE_PACKET__H +#define _K_PPP_CONFIGURE_PACKET__H + +#include +#include + + +typedef struct ppp_configure_item { + uint8 type; + uint8 length; + int8 data[0]; + // the data follows this structure +} ppp_configure_item; + + +class PPPConfigurePacket { + private: + // copies are not allowed! + PPPConfigurePacket(const PPPConfigurePacket& copy); + PPPConfigurePacket& operator= (const PPPConfigurePacket& copy); + + public: + PPPConfigurePacket(uint8 code); + PPPConfigurePacket(struct mbuf *packet); + ~PPPConfigurePacket(); + + bool SetCode(uint8 code); + uint8 Code() const + { return fCode; } + + void SetID(uint8 id) + { fID = id; } + uint8 ID() const + { return fID; } + + bool AddItem(const ppp_configure_item *item, int32 index = -1); + bool RemoveItem(ppp_configure_item *item); + int32 CountItems() const + { return fItems.CountItems(); } + ppp_configure_item *ItemAt(int32 index) const; + ppp_configure_item *ItemWithType(uint8 type) const; + + struct mbuf *ToMbuf(uint32 reserve = 0); + // the user is responsible for freeing the mbuf + + private: + uint8 fCode, fID; + List fItems; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDefs.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDefs.h new file mode 100644 index 0000000000..e1845c9209 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDefs.h @@ -0,0 +1,70 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_DEFS__H +#define _K_PPP_DEFS__H + +#include + + +typedef uint32 interface_id; + +// various constants +#define PPP_PULSE_RATE 500000 + + +// module key types (used when loading a module) +enum ppp_module_key_type { + PPP_UNDEFINED_KEY_TYPE = -1, + PPP_LOAD_MODULE_KEY_TYPE = 0, + PPP_DEVICE_KEY_TYPE, + PPP_PROTOCOL_KEY_TYPE, + PPP_AUTHENTICATOR_KEY_TYPE, + PPP_MULTILINK_KEY_TYPE +}; + +// PPP events as defined in RFC 1661 (with one exception: PPP_UP_FAILED_EVENT) +enum ppp_event { + PPP_UP_FAILED_EVENT, + PPP_UP_EVENT, + PPP_DOWN_EVENT, + PPP_OPEN_EVENT, + PPP_CLOSE_EVENT, + PPP_TO_GOOD_EVENT, + PPP_TO_BAD_EVENT, + PPP_RCR_GOOD_EVENT, + PPP_RCR_BAD_EVENT, + PPP_RCA_EVENT, + PPP_RCN_EVENT, + PPP_RTR_EVENT, + PPP_RTA_EVENT, + PPP_RUC_EVENT, + PPP_RXJ_GOOD_EVENT, + PPP_RXJ_BAD_EVENT, + PPP_RXR_EVENT +}; + +// LCP protocol codes as defined in RFC 1661 +// ToDo: add LCP extensions +enum ppp_lcp_code { + PPP_CONFIGURE_REQUEST = 1, + PPP_CONFIGURE_ACK = 2, + PPP_CONFIGURE_NAK = 3, + PPP_CONFIGURE_REJECT = 4, + PPP_TERMINATE_REQUEST = 5, + PPP_TERMINATE_ACK = 6, + PPP_CODE_REJECT = 7, + PPP_PROTOCOL_REJECT = 8, + PPP_ECHO_REQUEST = 9, + PPP_ECHO_REPLY = 10, + PPP_DISCARD_REQUEST = 11 +}; + +#define PPP_MIN_LCP_CODE PPP_CONFIGURE_REQUEST +#define PPP_MAX_LCP_CODE PPP_DISCARD_REQUEST + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDevice.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDevice.h new file mode 100644 index 0000000000..bd7525c3f4 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPDevice.h @@ -0,0 +1,92 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_DEVICE__H +#define _K_PPP_DEVICE__H + +#include + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + + +class PPPDevice { + public: + PPPDevice(const char *name, PPPInterface& interface, + driver_parameter *settings); + virtual ~PPPDevice(); + + virtual status_t InitCheck() const; + + const char *Name() const + { return fName; } + + PPPInterface& Interface() const + { return fInterface; } + driver_parameter *Settings() const + { return fSettings; } + + virtual status_t Control(uint32 op, void *data, size_t length); + + uint32 MTU() const + { return fMTU; } + + // these calls must not block + virtual void Up() = 0; + virtual void Down() = 0; + virtual void Listen() = 0; + 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(struct mbuf *packet) = 0; + // This should enqueue the packet and return immediately. + // The device is responsible for freeing the packet. + status_t PassToInterface(struct mbuf *packet); + // This will pass the packet to the interface's queue. + // Do not call Interface::ReceiveFromDevice directly + // if this can block a Send()! + + virtual void Pulse(); + + protected: + void SetMTU(uint32 MTU) + { fMTU = MTU; } + + // Report that we are going up/down + // (from now on, the Up() process can be aborted). + // Abort if false is returned! + bool UpStarted() const; + bool DownStarted() const; + + // report up/down events + void UpFailedEvent(); + void UpEvent(); + void DownEvent(); + + protected: + uint32 fMTU; + // always hold this value up-to-date! + bool fIsUp; + status_t fInitStatus; + + private: + char *fName; + PPPInterface& fInterface; + driver_parameter *fSettings; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPEncapsulator.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPEncapsulator.h new file mode 100644 index 0000000000..f007328635 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPEncapsulator.h @@ -0,0 +1,128 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_ENCAPSULATOR__H +#define _K_PPP_ENCAPSULATOR__H + +#include + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + + +class PPPEncapsulator { + public: + PPPEncapsulator(const char *name, ppp_phase phase, + ppp_encapsulation_level level, uint16 protocol, + int32 addressFamily, uint32 overhead, + PPPInterface& interface, driver_parameter *settings, + int32 flags = PPP_NO_FLAGS); + virtual ~PPPEncapsulator(); + + virtual status_t InitCheck() const; + + const char *Name() const + { return fName; } + + ppp_phase Phase() const + { return fPhase; } + + ppp_encapsulation_level Level() const + { return fLevel; } + uint32 Overhead() const + { return fOverhead; } + + PPPInterface& Interface() const + { return fInterface; } + driver_parameter *Settings() const + { return fSettings; } + + uint16 Protocol() const + { return fProtocol; } + int32 AddressFamily() const + { return fAddressFamily; } + // negative values and values > 0xFF are ignored + int32 Flags() const + { return fFlags; } + ppp_side Side() const + { return fSide; } + // which side this encapsulator works for + + void SetEnabled(bool enabled = true); + bool IsEnabled() const + { return fEnabled; } + + bool IsUpRequested() const + { return fUpRequested; } + + virtual status_t Control(uint32 op, void *data, size_t length); + virtual status_t StackControl(uint32 op, void *data); + // called by netstack (forwarded by PPPInterface) + + void SetNext(PPPEncapsulator *next) + { fNext = next; } + PPPEncapsulator *Next() const + { return fNext; } + + virtual bool Up() = 0; + virtual bool Down() = 0; + bool IsUp() const + { return fConnectionStatus == PPP_ESTABLISHED_PHASE; } + bool IsDown() const + { return fConnectionStatus == PPP_DOWN_PHASE; } + bool IsGoingUp() const + { return fConnectionStatus == PPP_ESTABLISHMENT_PHASE; } + bool IsGoingDown() const + { return fConnectionStatus == PPP_TERMINATION_PHASE; } + + virtual status_t Send(struct mbuf *packet, uint16 protocol) = 0; + virtual status_t Receive(struct mbuf *packet, uint16 protocol) = 0; + + status_t SendToNext(struct mbuf *packet, uint16 protocol) const; + // this will send your packet to the next (up) encapsulator + // if there is no next encapsulator (==NULL), it will + // call the interface's SendToDevice function + + virtual void Pulse(); + + protected: + void SetUpRequested(bool requested = true) + { fUpRequested = requested; } + + void UpStarted(); + void DownStarted(); + + void UpFailedEvent(); + void UpEvent(); + void DownEvent(); + // report up/down events + + protected: + uint32 fOverhead; + ppp_side fSide; + status_t fInitStatus; + + private: + char *fName; + ppp_phase fPhase; + ppp_encapsulation_level fLevel; + uint16 fProtocol; + int32 fAddressFamily; + PPPInterface& fInterface; + driver_parameter *fSettings; + int32 fFlags; + + PPPEncapsulator *fNext; + + bool fEnabled; + bool fUpRequested; + ppp_phase fConnectionStatus; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPInterface.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPInterface.h new file mode 100644 index 0000000000..1cf2546fb0 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPInterface.h @@ -0,0 +1,252 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_INTERFACE__H +#define _K_PPP_INTERFACE__H + +#include + +#include + +#ifndef _K_PPP_LCP__H +#include +#endif + +#include + +#ifndef _K_PPP_STATE_MACHINE__H +#include +#endif + +#include +#include + +class PPPDevice; +class PPPProtocol; +class PPPEncapsulator; +class PPPOptionHandler; + +struct ppp_manager_info; +struct ppp_module_info; + + +class PPPInterface { + friend class PPPStateMachine; + friend class PPPManager; + + private: + // copies are not allowed! + PPPInterface(const PPPInterface& copy); + PPPInterface& operator= (const PPPInterface& copy); + + // only PPPManager may construct us! + PPPInterface(interface_id ID, driver_settings *settings, + PPPInterface *parent = NULL); + ~PPPInterface(); + + public: + void Delete(); + + status_t InitCheck() const; + + interface_id ID() const + { return fID; } + + driver_settings* Settings() const + { return fSettings; } + + PPPStateMachine& StateMachine() + { return fStateMachine; } + PPPLCP& LCP() + { return fLCP; } + + struct ifnet *Ifnet() const + { return fIfnet; } + + struct ifq *InQueue() const + { return fInQueue; } + + // delays + uint32 DialRetryDelay() const + { return fDialRetryDelay; } + uint32 RedialDelay() const + { return fRedialDelay; } + + // idle handling + uint32 IdleSince() const + { return fIdleSince; } + uint32 DisconnectAfterIdleSince() const + { return fDisconnectAfterIdleSince; } + + bool SetMRU(uint32 MRU); + uint32 MRU() const + { return fMRU; } + // this is the smallest MRU that we and the peer have + bool SetInterfaceMTU(uint32 interfaceMTU) + { return SetMRU(interfaceMTU - fHeaderLength); } + uint32 InterfaceMTU() const + { return fInterfaceMTU; } + // this is the MRU including encapsulator overhead + + status_t Control(uint32 op, void *data, size_t length); + + bool SetDevice(PPPDevice *device); + PPPDevice *Device() const + { return fDevice; } + + bool AddProtocol(PPPProtocol *protocol); + bool RemoveProtocol(PPPProtocol *protocol); + int32 CountProtocols() const + { return fProtocols.CountItems(); } + PPPProtocol *ProtocolAt(int32 index) const; + PPPProtocol *ProtocolFor(uint16 protocol, int32 *start = NULL) const; + int32 IndexOfProtocol(PPPProtocol *protocol) const + { return fProtocols.IndexOf(protocol); } + + bool AddEncapsulator(PPPEncapsulator *encapsulator); + bool RemoveEncapsulator(PPPEncapsulator *encapsulator); + int32 CountEncapsulators() const; + PPPEncapsulator *EncapsulatorAt(int32 index) const; + PPPEncapsulator *FirstEncapsulator() const + { return fFirstEncapsulator; } + PPPEncapsulator *EncapsulatorFor(uint16 protocol, + PPPEncapsulator *start = NULL) const; + + // multilink methods + bool AddChild(PPPInterface *child); + bool RemoveChild(PPPInterface *child); + int32 CountChildren() const + { return fChildren.CountItems(); } + PPPInterface *ChildAt(int32 index) const; + PPPInterface *Parent() const + { return fParent; } + bool IsMultilink() const + { return fIsMultilink; } + + void SetAutoRedial(bool autoRedial = true); + bool DoesAutoRedial() const + { return fAutoRedial; } + + void SetDialOnDemand(bool dialOnDemand = true); + bool DoesDialOnDemand() const + { return fDialOnDemand; } + + ppp_mode Mode() const + { return fMode; } + // client or server mode? + ppp_state State() const + { return fStateMachine.State(); } + ppp_phase Phase() const + { return fStateMachine.Phase(); } + + // Protocol-Field-Compression + bool SetPFCOptions(uint8 pfcOptions); + uint8 PFCOptions() const + { return fPFCOptions; } + ppp_pfc_state LocalPFCState() const + { return fLocalPFCState; } + // the local PFC state says if we accepted a request from the peer + // i.e.: we may use PFC in outgoing packets + ppp_pfc_state PeerPFCState() const + { return fPeerPFCState; } + // the peer PFC state says if the peer accepted a request us + // i.e.: the peer might send PFC-compressed packets to us + bool UseLocalPFC() const + { return LocalPFCState() & PPP_PFC_ACCEPTED; } + + bool Up(); + // in server mode Up() listens for an incoming connection + bool Down(); + bool IsUp() const; + + PPPReportManager& ReportManager() + { return fReportManager; } + bool Report(ppp_report_type type, int32 code, void *data, int32 length) + { return fReportManager.Report(type, code, data, length); } + // returns false if reply was bad (or an error occured) + + bool LoadModules(driver_settings *settings, + int32 start, int32 count); + bool LoadModule(const char *name, driver_parameter *parameter, + ppp_module_key_type type); + + status_t Send(struct mbuf *packet, uint16 protocol); + status_t Receive(struct mbuf *packet, uint16 protocol); + + status_t SendToDevice(struct mbuf *packet, uint16 protocol); + status_t ReceiveFromDevice(struct mbuf *packet); + // This is called by the receive-thread. + // Only call this if it does not block Send() or + // SendToDevice()! + + void Pulse(); + // this manages all timeouts, etc. + + private: + bool RegisterInterface(); + // adds us to the manager module and + // saves the returned ifnet structure + bool UnregisterInterface(); + + status_t StackControl(uint32 op, void *data); + // stack routes ioctls to interface + status_t StackControlEachHandler(uint32 op, void *data); + // this calls StackControl() with the given parameters for each handler + + void CalculateInterfaceMTU(); + void CalculateBaudRate(); + + void Redial(uint32 delay); + + // multilink methods + void SetParent(PPPInterface *parent) + { fParent = parent; } + + private: + interface_id fID; + // the manager assigns an ID to every interface + driver_settings *fSettings; + PPPStateMachine fStateMachine; + PPPLCP fLCP; + PPPReportManager fReportManager; + struct ifnet *fIfnet; + + thread_id fUpThread, fInQueueThread; + struct ifq *fInQueue; + + thread_id fRedialThread; + uint32 fDialRetry, fDialRetriesLimit; + uint32 fDialRetryDelay, fRedialDelay; + + ppp_manager_info *fManager; + + uint32 fIdleSince, fDisconnectAfterIdleSince; + uint32 fMRU, fInterfaceMTU, fHeaderLength; + + PPPInterface *fParent; + List fChildren; + bool fIsMultilink; + + bool fAutoRedial, fDialOnDemand; + + ppp_mode fMode; + ppp_pfc_state fLocalPFCState, fPeerPFCState; + uint8 fPFCOptions; + + PPPDevice *fDevice; + PPPEncapsulator *fFirstEncapsulator; + List fProtocols; + List fModules; + + BLocker& fLock; + + status_t fInitStatus; + int32 fDeleteCounter; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCP.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCP.h new file mode 100644 index 0000000000..b80e692619 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCP.h @@ -0,0 +1,96 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_LCP__H +#define _K_PPP_LCP__H + +#include + +#ifndef _K_PPP_PROTOCOL__H +#include +#endif + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + +#ifndef _K_PPP_STATE_MACHINE__H +#include +#endif + +class PPPEncapsulator; +class PPPLCPExtension; +class PPPOptionHandler; + + +typedef struct ppp_lcp_packet { + uint8 code; + uint8 id; + uint16 length; + uint8 data[0]; +} ppp_lcp_packet; + + +class PPPLCP : public PPPProtocol { + friend class PPPInterface; + + private: + // may only be constructed/destructed by PPPInterface + PPPLCP(PPPInterface& interface); + virtual ~PPPLCP(); + + // copies are not allowed! + PPPLCP(const PPPLCP& copy); + PPPLCP& operator= (const PPPLCP& copy); + + public: + PPPStateMachine& StateMachine() const + { return fStateMachine; } + + bool AddOptionHandler(PPPOptionHandler *handler); + bool RemoveOptionHandler(PPPOptionHandler *handler); + int32 CountOptionHandlers() const + { return fOptionHandlers.CountItems(); } + PPPOptionHandler *OptionHandlerAt(int32 index) const; + PPPOptionHandler *OptionHandlerFor(uint8 type, int32 *start = NULL) const; + + bool AddLCPExtension(PPPLCPExtension *extension); + bool RemoveLCPExtension(PPPLCPExtension *extension); + int32 CountLCPExtensions() const + { return fLCPExtensions.CountItems(); } + PPPLCPExtension *LCPExtensionAt(int32 index) const; + PPPLCPExtension *LCPExtensionFor(uint8 code, int32 *start = NULL) const; + + PPPEncapsulator *Target() const + { return fTarget; } + void SetTarget(PPPEncapsulator *target) + { fTarget = target; } + // if target != all packtes will be passed to the encapsulator + // instead of the interface/device + + uint32 AdditionalOverhead() const; + // the overhead caused by the target, the device, and the interface + + virtual bool Up(); + virtual bool Down(); + + virtual status_t Send(struct mbuf *packet); + virtual status_t Receive(struct mbuf *packet, uint16 protocol); + + virtual void Pulse(); + + private: + PPPStateMachine& fStateMachine; + + List fOptionHandlers; + List fLCPExtensions; + + PPPEncapsulator *fTarget; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCPExtension.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCPExtension.h new file mode 100644 index 0000000000..0400e1dafa --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPLCPExtension.h @@ -0,0 +1,64 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef __K_PPP_LCP_EXTENSION__H +#define __K_PPP_LCP_EXTENSION__H + +#include + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + + +class PPPLCPExtension { + public: + PPPLCPExtension(const char *name, uint8 code, PPPInterface& interface, + driver_parameter *settings); + virtual ~PPPLCPExtension(); + + virtual status_t InitCheck() const; + + const char *Name() const + { return fName; } + + PPPInterface& Interface() const + { return fInterface; } + driver_parameter *Settings() const + { return fSettings; } + + void SetEnabled(bool enabled = true) + { fEnabled = enabled; } + bool IsEnabled() const + { return fEnabled; } + + uint8 Code() const + { return fCode; } + + virtual status_t Control(uint32 op, void *data, size_t length); + virtual status_t StackControl(uint32 op, void *data); + // called by netstack (forwarded by PPPInterface) + + virtual status_t Receive(struct mbuf *packet, uint8 code) = 0; + + virtual void Reset(); + virtual void Pulse(); + + protected: + status_t fInitStatus; + + private: + char *fName; + PPPInterface& fInterface; + driver_parameter *fSettings; + uint8 fCode; + + bool fEnabled; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPManager.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPManager.h new file mode 100644 index 0000000000..ecd38ef893 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPManager.h @@ -0,0 +1,48 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_MANAGER__H +#define _K_PPP_MANAGER__H + +#include "net_module.h" + + +#define PPP_MANAGER_MODULE_NAME "network/interfaces/ppp" + +#define PPP_UNDEFINED_INTERFACE_ID 0 + // create_interface() returns this value on failure + +// this allows you to ask for specific interface_ids +enum ppp_interface_filter { + PPP_ALL_INTERFACES, + PPP_REGISTERED_INTERFACES, + PPP_UNREGISTERED_INTERFACES +}; + +typedef struct ppp_manager_info { + kernel_net_module_info knminfo; + + interface_id (*create_interface)(const driver_settings *settings, + interface_id parent); + // you should always create interfaces using this function + void (*delete_interface)(interface_id ID); + // this marks the interface for deletion + void (*remove_interface)(interface_id ID); + // remove the interface from database (make sure you can delete it yourself!) + + ifnet* (*register_interface)(interface_id ID); + bool (*unregister_interface)(interface_id ID); + + status_t (*control)(interface_id ID, uint32 op, void *data, size_t length); + + status_t (*get_interfaces)(interface_id **interfaces, uint32 *count, + ppp_interface_filter filter = PPP_REGISTERED_INTERFACES); + // the user is responsible for free()'ing the interface_id array +} ppp_manager_info; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPModule.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPModule.h new file mode 100644 index 0000000000..c3f24e6c98 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPModule.h @@ -0,0 +1,28 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_MODULE__H +#define _K_PPP_MODULE__H + +#include + +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& mainInterface, PPPInterface *subInterface, + driver_parameter *settings, ppp_module_key_type type); + // multilink: handlers that must run on a real device + // should be added to subInterface (may be NULL) + // while mainInterface handlers are used for the + // bundle of interfaces +} ppp_module_info; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPOptionHandler.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPOptionHandler.h new file mode 100644 index 0000000000..fb00c998fe --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPOptionHandler.h @@ -0,0 +1,81 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_OPTION_HANDLER__H +#define _K_PPP_OPTION_HANDLER__H + +#include + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + +class PPPConfigurePacket; + + +class PPPOptionHandler { + public: + PPPOptionHandler(const char *name, uint8 type, PPPInterface& interface, + driver_parameter *settings); + virtual ~PPPOptionHandler(); + + virtual status_t InitCheck() const; + + const char *Name() const + { return fName; } + + uint8 Type() const + { return fType; } + + PPPInterface& Interface() const + { return fInterface; } + driver_parameter *Settings() const + { return fSettings; } + + void SetEnabled(bool enabled = true) + { fEnabled = enabled; } + bool IsEnabled() const + { return fEnabled; } + + virtual status_t Control(uint32 op, void *data, size_t length); + virtual status_t StackControl(uint32 op, void *data); + // called by netstack (forwarded by PPPInterface) + + // we want to send a configure request or we received a reply + virtual status_t AddToRequest(PPPConfigurePacket& request) = 0; + virtual status_t ParseNak(const PPPConfigurePacket& nak) = 0; + // create next request based on these and previous values + virtual status_t ParseReject(const PPPConfigurePacket& reject) = 0; + // create next request based on these and previous values + virtual status_t ParseAck(const PPPConfigurePacket& ack) = 0; + // this is called for all handlers + + // peer sent configure request + virtual status_t ParseRequest(const PPPConfigurePacket& request, + int32 index, PPPConfigurePacket& nak, PPPConfigurePacket& reject) = 0; + // index may be behind the last item which means additional values can be + // appended + virtual status_t SendingAck(const PPPConfigurePacket& ack) = 0; + // notification that we ack these values + + virtual void Reset() = 0; + // e.g.: remove list of rejected values + + protected: + status_t fInitStatus; + + private: + char *fName; + uint8 fType; + PPPInterface& fInterface; + driver_parameter *fSettings; + + bool fEnabled; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPProtocol.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPProtocol.h new file mode 100644 index 0000000000..ca7efb0bbe --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPProtocol.h @@ -0,0 +1,116 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_PROTOCOL__H +#define _K_PPP_PROTOCOL__H + +#include + +class PPPInterface; +class PPPOptionHandler; + + +class PPPProtocol { + public: + PPPProtocol(const char *name, ppp_phase phase, uint16 protocol, + int32 addressFamily, PPPInterface& interface, + driver_parameter *settings, int32 flags = PPP_NO_FLAGS, + const char *type = NULL, PPPOptionHandler *optionHandler = NULL); + virtual ~PPPProtocol(); + + virtual status_t InitCheck() const; + + const char *Name() const + { return fName; } + + ppp_phase Phase() const + { return fPhase; } + + PPPInterface& Interface() const + { return fInterface; } + driver_parameter *Settings() const + { return fSettings; } + + uint16 Protocol() const + { return fProtocol; } + int32 AddressFamily() const + { return fAddressFamily; } + // negative values and values > 0xFF are ignored + int32 Flags() const + { return fFlags; } + ppp_side Side() const + { return fSide; } + // which side this protocol works for + + // extensions + const char *Type() const + { return fType; } + PPPOptionHandler *OptionHandler() const + { return fOptionHandler; } + + void SetEnabled(bool enabled = true); + bool IsEnabled() const + { return fEnabled; } + + bool IsUpRequested() const + { return fUpRequested; } + + virtual status_t Control(uint32 op, void *data, size_t length); + virtual status_t StackControl(uint32 op, void *data); + // called by netstack (forwarded by PPPInterface) + + virtual bool Up() = 0; + virtual bool Down() = 0; + // if DialOnDemand is supported check for DialOnDemand settings change + bool IsUp() const + { return fConnectionStatus == PPP_ESTABLISHED_PHASE; } + bool IsDown() const + { return fConnectionStatus == PPP_DOWN_PHASE; } + bool IsGoingUp() const + { return fConnectionStatus == PPP_ESTABLISHMENT_PHASE; } + bool IsGoingDown() const + { return fConnectionStatus == PPP_TERMINATION_PHASE; } + + virtual status_t Send(struct mbuf *packet) = 0; + virtual status_t Receive(struct mbuf *packet, uint16 protocol) = 0; + + virtual void Pulse(); + + protected: + void SetUpRequested(bool requested = true) + { fUpRequested = requested; } + + void UpStarted(); + void DownStarted(); + + void UpFailedEvent(); + void UpEvent(); + void DownEvent(); + // report up/down events + + protected: + ppp_side fSide; + status_t fInitStatus; + + private: + char *fName; + ppp_phase fPhase; + uint16 fProtocol; + int32 fAddressFamily; + PPPInterface& fInterface; + driver_parameter *fSettings; + int32 fFlags; + char *fType; + PPPOptionHandler *fOptionHandler; + + bool fEnabled; + bool fUpRequested; + ppp_phase fConnectionStatus; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPReportManager.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPReportManager.h new file mode 100644 index 0000000000..660c61540a --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPReportManager.h @@ -0,0 +1,39 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_REPORT_MANAGER__H +#define _K_PPP_REPORT_MANAGER__H + +#include + +#include +#include + +#include + + +#define PPP_REPLY(sender, value) send_data_with_timeout((sender), (value), NULL, 0, PPP_REPORT_TIMEOUT) + +class PPPReportManager { + public: + PPPReportManager(BLocker& lock); + ~PPPReportManager(); + + void EnableReports(ppp_report_type type, thread_id thread, + int32 flags = PPP_NO_FLAGS); + void DisableReports(ppp_report_type type, thread_id thread); + bool DoesReport(ppp_report_type type, thread_id thread); + bool Report(ppp_report_type type, int32 code, void *data, int32 length); + // returns false if reply was bad (or an error occured) + + private: + BLocker& fLock; + List fReportRequests; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPStateMachine.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPStateMachine.h new file mode 100644 index 0000000000..1939eb669c --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPStateMachine.h @@ -0,0 +1,177 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_STATE_MACHINE__H +#define _K_PPP_STATE_MACHINE__H + +#include + +class PPPEncapsulator; +class PPPProtocol; + +#ifndef _K_PPP_INTERFACE__H +#include +#endif + +#include + + +class PPPStateMachine { + friend class PPPInterface; + friend class PPPLCP; + + private: + // may only be constructed/destructed by PPPInterface + PPPStateMachine(PPPInterface& interface); + ~PPPStateMachine(); + + // copies are not allowed! + PPPStateMachine(const PPPStateMachine& copy); + PPPStateMachine& operator= (const PPPStateMachine& copy); + + public: + PPPInterface& Interface() const + { return fInterface; } + PPPLCP& LCP() const + { return fLCP; } + + ppp_state State() const + { return fState; } + ppp_phase Phase() const + { return fPhase; } + + uint8 NextID(); + // return the next id for LCP packets + + void SetMagicNumber(uint32 magicNumber) + { fMagicNumber = magicNumber; } + uint32 MagicNumber() const + { return fMagicNumber; } + + // public actions + bool Reconfigure(); + bool SendEchoRequest(); + bool SendDiscardRequest(); + + // public events + void LocalAuthenticationRequested(); + void LocalAuthenticationAccepted(const char *name); + void LocalAuthenticationDenied(const char *name); + const char *LocalAuthenticationName() const + { return fLocalAuthenticationName; } + ppp_authentication_status LocalAuthenticationStatus() const + { return fLocalAuthenticationStatus; } + + void PeerAuthenticationRequested(); + void PeerAuthenticationAccepted(const char *name); + void PeerAuthenticationDenied(const char *name); + const char *PeerAuthenticationName() const + { return fPeerAuthenticationName; } + ppp_authentication_status PeerAuthenticationStatus() const + { return fPeerAuthenticationStatus; } + + // sub-interface events + void UpFailedEvent(PPPInterface& interface); + void UpEvent(PPPInterface& interface); + void DownEvent(PPPInterface& interface); + + // protocol events + void UpFailedEvent(PPPProtocol *protocol); + void UpEvent(PPPProtocol *protocol); + void DownEvent(PPPProtocol *protocol); + + // encapsulator events + void UpFailedEvent(PPPEncapsulator *encapsulator); + void UpEvent(PPPEncapsulator *encapsulator); + void DownEvent(PPPEncapsulator *encapsulator); + + // device events + bool TLSNotify(); + bool TLFNotify(); + void UpFailedEvent(); + void UpEvent(); + void DownEvent(); + + private: + BLocker& Locker() + { return fLock; } + + // private StateMachine methods + void NewState(ppp_state next); + void NewPhase(ppp_phase next); + + // private events + void OpenEvent(); + void CloseEvent(); + void TOGoodEvent(); + void TOBadEvent(); + void RCRGoodEvent(struct mbuf *packet); + void RCRBadEvent(struct mbuf *nak, struct mbuf *reject); + void RCAEvent(struct mbuf *packet); + void RCNEvent(struct mbuf *packet); + void RTREvent(struct mbuf *packet); + void RTAEvent(struct mbuf *packet); + void RUCEvent(struct mbuf *packet, uint16 protocol, + uint8 code = PPP_PROTOCOL_REJECT); + void RXJGoodEvent(struct mbuf *packet); + void RXJBadEvent(struct mbuf *packet); + void RXREvent(struct mbuf *packet); + + // general events (for Good/Bad events) + void TimerEvent(); + void RCREvent(struct mbuf *packet); + void RXJEvent(struct mbuf *packet); + + // actions + void IllegalEvent(ppp_event event); + void ThisLayerUp(); + void ThisLayerDown(); + void ThisLayerStarted(); + void ThisLayerFinished(); + void InitializeRestartCount(); + void ZeroRestartCount(); + bool SendConfigureRequest(); + bool SendConfigureAck(struct mbuf *packet); + bool SendConfigureNak(struct mbuf *packet); + bool SendTerminateRequest(); + bool SendTerminateAck(struct mbuf *request = NULL); + bool SendCodeReject(struct mbuf *packet, uint16 protocol, uint8 code); + bool SendEchoReply(struct mbuf *request); + + void BringHandlersUp(); + uint32 BringPhaseUp(); + + void DownProtocols(); + void DownEncapsulators(); + void ResetLCPHandlers(); + + private: + PPPInterface& fInterface; + PPPLCP& fLCP; + + ppp_phase fPhase; + ppp_state fState; + + vint32 fID; + uint32 fMagicNumber; + + ppp_authentication_status fLocalAuthenticationStatus, + fPeerAuthenticationStatus; + char *fLocalAuthenticationName, *fPeerAuthenticationName; + + BLocker fLock; + + // counters and timers + int32 fMaxRequest, fMaxTerminate, fMaxNak; + int32 fRequestCounter, fTerminateCounter, fNakCounter; + uint8 fRequestID, fTerminateID, fEchoID; + // the ID we used for the last configure/terminate/echo request + bigtime_t fNextTimeout; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPUtils.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPUtils.h new file mode 100644 index 0000000000..87289cd21d --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/KPPPUtils.h @@ -0,0 +1,55 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _K_PPP_UTILS__H +#define _K_PPP_UTILS__H + +#ifndef _K_PPP_DEFS__H +#include +#endif + + +// helper functions +template +inline +bool +is_handler_allowed(T& handler, ppp_state state, ppp_phase phase) +{ + if(handler.Protocol() == PPP_LCP_PROTOCOL) + return true; + else if(state != PPP_OPENED_STATE) + return false; + else if(phase > PPP_AUTHENTICATION_PHASE + || (phase >= PPP_ESTABLISHMENT_PHASE + && handler.Flags() & PPP_ALWAYS_ALLOWED)) + return true; + else + return false; +} + +// the list template does not support iterating over each item :( +// this template iterates over each item in an indexed list +template +inline +void +ForEachItem(_LIST& list, _FUNCTION function) +{ + for(int index = 0; index < list.CountItems(); index++) + function(list.ItemAt(index)); +} + + +// These are very simple send/receive_data functions with a timeout +// and there is a race condition beween has_data() and send/receive_data(). +// Timeouts in ms. +status_t send_data_with_timeout(thread_id thread, int32 code, void *buffer, + size_t buffer_size, uint32 timeout); +status_t receive_data_with_timeout(thread_id *sender, int32 *code, void *buffer, + size_t buffer_size, uint32 timeout); + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/List.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/List.h new file mode 100644 index 0000000000..21aeb87533 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/List.h @@ -0,0 +1,385 @@ +// List.h +// +// Copyright (c) 2003, Ingo Weinhold (bonefish@cs.tu-berlin.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// Except as contained in this notice, the name of a copyright holder shall +// not be used in advertising or otherwise to promote the sale, use or other +// dealings in this Software without prior written authorization of the +// copyright holder. + +#ifndef LIST_H +#define LIST_H + +#include +#include +#include + +#include + +template +class DefaultDefaultItemCreator { +public: + static inline ITEM GetItem() { return ITEM(0); } +}; + +/*! + \class List + \brief A generic list implementation. +*/ +template > +class List { +public: + typedef ITEM item_t; + typedef List list_t; + +private: + static item_t sDefaultItem; + static const size_t kDefaultChunkSize = 10; + static const size_t kMaximalChunkSize = 1024 * 1024; + +public: + List(size_t chunkSize = kDefaultChunkSize); + ~List(); + + inline const item_t &GetDefaultItem() const; + inline item_t &GetDefaultItem(); + + bool AddItem(const item_t &item, int32 index); + bool AddItem(const item_t &item); +// bool AddList(list_t *list, int32 index); +// bool AddList(list_t *list); + + bool RemoveItem(const item_t &item); + bool RemoveItem(int32 index); + + bool ReplaceItem(int32 index, const item_t &item); + + bool MoveItem(int32 oldIndex, int32 newIndex); + + void MakeEmpty(); + + int32 CountItems() const; + bool IsEmpty() const; + const item_t &ItemAt(int32 index) const; + item_t &ItemAt(int32 index); + const item_t *Items() const; + int32 IndexOf(const item_t &item) const; + bool HasItem(const item_t &item) const; + + // debugging + int32 GetCapacity() const { return fCapacity; } + +private: + inline static void _MoveItems(item_t* items, int32 offset, int32 count); + bool _Resize(size_t count); + +private: + size_t fCapacity; + size_t fChunkSize; + int32 fItemCount; + item_t *fItems; +}; + +// sDefaultItem +template +List::item_t + List::sDefaultItem( + DEFAULT_ITEM_SUPPLIER::GetItem()); + +// constructor +template +List::List(size_t chunkSize) + : fCapacity(0), + fChunkSize(chunkSize), + fItemCount(0), + fItems(NULL) +{ + if (fChunkSize == 0 || fChunkSize > kMaximalChunkSize) + fChunkSize = kDefaultChunkSize; + _Resize(0); +} + +// destructor +template +List::~List() +{ + MakeEmpty(); + free(fItems); +} + +// GetDefaultItem +template +inline +const List::item_t & +List::GetDefaultItem() const +{ + return sDefaultItem; +} + +// GetDefaultItem +template +inline +List::item_t & +List::GetDefaultItem() +{ + return sDefaultItem; +} + +// _MoveItems +template +inline +void +List::_MoveItems(item_t* items, int32 offset, int32 count) +{ + if (count > 0 && offset != 0) + memmove(items + offset, items, count * sizeof(item_t)); +} + +// AddItem +template +bool +List::AddItem(const item_t &item, int32 index) +{ + bool result = (index >= 0 && index <= fItemCount + && _Resize(fItemCount + 1)); + if (result) { + _MoveItems(fItems + index, 1, fItemCount - index - 1); + new(fItems + index) item_t(item); + } + return result; +} + +// AddItem +template +bool +List::AddItem(const item_t &item) +{ + bool result = true; + if ((int32)fCapacity > fItemCount) { + new(fItems + fItemCount) item_t(item); + fItemCount++; + } else { + if ((result = _Resize(fItemCount + 1))) + new(fItems + (fItemCount - 1)) item_t(item); + } + return result; +} + +// These don't use the copy constructor! +/* +// AddList +template +bool +List::AddList(list_t *list, int32 index) +{ + bool result = (list && index >= 0 && index <= fItemCount); + if (result && list->fItemCount > 0) { + int32 count = list->fItemCount; + result = _Resize(fItemCount + count); + if (result) { + _MoveItems(fItems + index, count, fItemCount - index - count); + memcpy(fItems + index, list->fItems, + list->fItemCount * sizeof(item_t)); + } + } + return result; +} + +// AddList +template +bool +List::AddList(list_t *list) +{ + bool result = (list); + if (result && list->fItemCount > 0) { + int32 index = fItemCount; + int32 count = list->fItemCount; + result = _Resize(fItemCount + count); + if (result) { + memcpy(fItems + index, list->fItems, + list->fItemCount * sizeof(item_t)); + } + } + return result; +} +*/ + +// RemoveItem +template +bool +List::RemoveItem(const item_t &item) +{ + int32 index = IndexOf(item); + bool result = (index >= 0); + if (result) + RemoveItem(index); + return result; +} + +// RemoveItem +template +bool +List::RemoveItem(int32 index) +{ + if (index >= 0 && index < fItemCount) { + fItems[index].~item_t(); + _MoveItems(fItems + index + 1, -1, fItemCount - index - 1); + _Resize(fItemCount - 1); + return true; + } + return false; +} + +// ReplaceItem +template +bool +List::ReplaceItem(int32 index, const item_t &item) +{ + if (index >= 0 && index < fItemCount) { + fItems[index] = item; + return true; + } + return false; +} + +// MoveItem +template +bool +List::MoveItem(int32 oldIndex, int32 newIndex) +{ + if (oldIndex >= 0 && oldIndex < fItemCount + && newIndex >= 0 && newIndex <= fItemCount) { + if (oldIndex < newIndex - 1) { + item_t item = fItems[oldIndex]; + _MoveItems(fItems + oldIndex + 1, -1, newIndex - oldIndex - 1); + fItems[newIndex] = item; + } else if (oldIndex > newIndex) { + item_t item = fItems[oldIndex]; + _MoveItems(fItems + newIndex, 1, oldIndex - newIndex); + fItems[newIndex] = item; + } + return true; + } + return false; +} + +// MakeEmpty +template +void +List::MakeEmpty() +{ + for (int32 i = 0; i < fItemCount; i++) + fItems[i].~item_t(); + _Resize(0); +} + +// CountItems +template +int32 +List::CountItems() const +{ + return fItemCount; +} + +// IsEmpty +template +bool +List::IsEmpty() const +{ + return (fItemCount == 0); +} + +// ItemAt +template +const List::item_t & +List::ItemAt(int32 index) const +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + return sDefaultItem; +} + +// ItemAt +template +List::item_t & +List::ItemAt(int32 index) +{ + if (index >= 0 && index < fItemCount) + return fItems[index]; + return sDefaultItem; +} + +// Items +template +const List::item_t * +List::Items() const +{ + return fItems; +} + +// IndexOf +template +int32 +List::IndexOf(const item_t &item) const +{ + for (int32 i = 0; i < fItemCount; i++) { + if (fItems[i] == item) + return i; + } + return -1; +} + +// HasItem +template +bool +List::HasItem(const item_t &item) const +{ + return (IndexOf(item) >= 0); +} + +// _Resize +template +bool +List::_Resize(size_t count) +{ + bool result = true; + // calculate the new capacity + int32 newSize = count; + if (newSize <= 0) + newSize = 1; + newSize = ((newSize - 1) / fChunkSize + 1) * fChunkSize; + // resize if necessary + if ((size_t)newSize != fCapacity) { + item_t* newItems + = (item_t*)realloc(fItems, newSize * sizeof(item_t)); + if (newItems) { + fItems = newItems; + fCapacity = newSize; + } else + result = false; + } + if (result) + fItemCount = count; + return result; +} + +#endif // LIST_H diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/LockerHelper.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/LockerHelper.h new file mode 100644 index 0000000000..132ce8275b --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/LockerHelper.h @@ -0,0 +1,56 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +/* + * LockerHelper.h + * + * The LockerHelper acquires a lock on construction and releases it + * on destruction. + * This is a very useful class because you do not have to worry about + * releasing the lock at every possible point. It is done automatically. + * + */ + + +#ifndef _LOCKER_HELPER__H +#define _LOCKER_HELPER__H + +#include + + +class LockerHelper { + private: + // copies are not allowed! + LockerHelper(const LockerHelper& copy); + LockerHelper& operator= (const LockerHelper& copy); + + public: + LockerHelper(BLocker& lock) : fLock(&lock) + { + if(!fLock->Lock()) + fLock = NULL; + } + + ~LockerHelper() + { + if(fLock) + fLock->Unlock(); + } + + void UnlockNow() + { + if(fLock) + fLock->Unlock(); + fLock = NULL; + } + + private: + BLocker *fLock; +}; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPControl.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPControl.h new file mode 100644 index 0000000000..b92821dad5 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPControl.h @@ -0,0 +1,181 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _PPP_CONTROL__H +#define _PPP_CONTROL__H + +#include +#include + + +// various constants +#define PPP_HANDLER_NAME_LENGTH_LIMIT 63 + // if the name is longer than this value it will be truncated to fit the structure + +// starting values and other values for control ops +#define PPP_RESERVE_OPS_COUNT 0xFFFF +#define PPP_OPS_START B_DEVICE_OP_CODES_END + 1 +#define PPP_INTERFACE_OPS_START PPP_OPS_START + PPP_RESERVE_OPS_COUNT +#define PPP_DEVICE_OPS_START PPP_OPS_START + 2 * PPP_RESERVE_OPS_COUNT +#define PPP_PROTOCOL_OPS_START PPP_OPS_START + 3 * PPP_RESERVE_OPS_COUNT +#define PPP_ENCAPSULATOR_OPS_START PPP_OPS_START + 4 * PPP_RESERVE_OPS_COUNT +#define PPP_OPTION_HANDLER_OPS_START PPP_OPS_START + 5 * PPP_RESERVE_OPS_COUNT +#define PPP_LCP_EXTENSION_OPS_START PPP_OPS_START + 6 * PPP_RESERVE_OPS_COUNT +#define PPP_COMMON_OPS_START PPP_OPS_START + 10 * PPP_RESERVE_OPS_COUNT +#define PPP_USER_OPS_START PPP_OPS_START + 32 * PPP_RESERVE_OPS_COUNT + + +enum ppp_control_ops { + // ----------------------------------------------------- + // PPPInterface + PPPC_GET_INTERFACE_INFO = PPP_INTERFACE_OPS_START, + PPPC_SET_MRU, + PPPC_SET_DIAL_ON_DEMAND, + PPPC_SET_AUTO_REDIAL, + + // these control ops use the ppp_report_request structure + PPPC_ENABLE_INTERFACE_REPORTS, + PPPC_DISABLE_INTERFACE_REPORTS, + // flags are not used for this control op + + // handler access + PPPC_CONTROL_DEVICE, + PPPC_CONTROL_PROTOCOL, + PPPC_CONTROL_ENCAPSULATOR, + PPPC_CONTROL_OPTION_HANDLER, + PPPC_CONTROL_LCP_EXTENSION, + PPPC_CONTROL_CHILD, + // ----------------------------------------------------- + + // ----------------------------------------------------- + // PPPDevice + PPPC_GET_DEVICE_INFO = PPP_DEVICE_OPS_START, + // ----------------------------------------------------- + + // ----------------------------------------------------- + // Common/mixed ops + PPPC_GET_HANDLER_INFO = PPP_COMMON_OPS_START, + // PPPProtocol and PPPEncapsulator + PPPC_SET_ENABLED, + PPPC_GET_SIMPLE_HANDLER_INFO, + // PPPOptionHandler and PPPLCPExtension + // ----------------------------------------------------- + + PPP_CONTROL_OPS_END = B_DEVICE_OP_CODES_END + 0xFFFF +}; + + +typedef struct ppp_control_info { + uint32 index; + // index of interface/protocol/encapsulator/etc. + uint32 op; + // the Control()/ioctl() opcode + void *data; + size_t length; + // should always be set +} ppp_control_info; + + +// ----------------------------------------------------------- +// structures for storing information about interface/handlers +// use the xxx_info_t structures when allocating memory (they +// reserve memory for future implementations) +// ----------------------------------------------------------- +#define _PPP_INFO_T_SIZE_ 256 + +typedef struct ppp_interface_info { + const driver_settings *settings; + + ppp_mode mode; + ppp_state state; + ppp_phase phase; + ppp_authentication_status localAuthenticationStatus, peerAuthenticationStatus; + ppp_pfc_state localPFCState, peerPFCState; + uint8 pfcOptions; + + uint32 protocolsCount, encapsulatorsCount, optionHandlersCount, + LCPExtensionsCount, childrenCount; + uint32 MRU, interfaceMTU; + + uint32 dialRetry, dialRetriesLimit; + uint32 dialRetryDelay, redialDelay; + uint32 idleSince, disconnectAfterIdleSince; + + bool doesDialOnDemand, doesAutoRedial, hasDevice, isMultilink, hasParent; +} ppp_interface_info; +typedef struct ppp_interface_info_t { + ppp_interface_info info; + uint8 _reserved_[_PPP_INFO_T_SIZE_ - sizeof(ppp_interface_info)]; +} ppp_interface_info_t; + + +// devices are special handlers, so they have their own structure +typedef struct ppp_device_info { + char name[PPP_HANDLER_NAME_LENGTH_LIMIT + 1]; + + const driver_parameter *settings; + + uint32 MTU; + uint32 inputTransferRate, outputTransferRate, outputBytesCount; +} ppp_device_info; +typedef struct ppp_device_info_t { + ppp_device_info info; + uint8 _reserved_[_PPP_INFO_T_SIZE_ - sizeof(ppp_device_info)]; +} ppp_device_info_t; + + +typedef struct ppp_handler_info { + char name[PPP_HANDLER_NAME_LENGTH_LIMIT + 1]; + + // general + const driver_parameter *settings; + + ppp_phase phase; + int32 addressFamily, flags; + ppp_side side; + uint16 protocol; + + bool isEnabled; + + // only protocol and encapsulator + bool isUpRequested; + ppp_phase connectionStatus; + // there are four possible states: + // PPP_ESTABLISHED_PHASE - IsUp() == true + // PPP_DOWN_PHASE - IsDown() == true + // PPP_ESTABLISHMENT_PHASE - IsGoingUp() == true + // PPP_TERMINATION_PHASE - IsGoingDown() == true + + // only protocol + char type[PPP_HANDLER_NAME_LENGTH_LIMIT + 1]; + + // only encapsulator + ppp_encapsulation_level level; + uint32 overhead; +} ppp_handler_info; +typedef struct ppp_handler_info_t { + ppp_handler_info info; + uint8 _reserved_[_PPP_INFO_T_SIZE_ - sizeof(ppp_handler_info)]; +} ppp_handler_info_t; + + +typedef struct ppp_simple_handler_info { + char name[PPP_HANDLER_NAME_LENGTH_LIMIT + 1]; + + const driver_parameter *settings; + bool isEnabled; + + uint8 code; + // only PPPLCPExtension +} ppp_simple_handler_info; +typedef struct ppp_simple_handler_info_t { + ppp_simple_handler_info info; + uint8 _reserved_[_PPP_INFO_T_SIZE_ - sizeof(ppp_simple_handler_info)]; +} ppp_simple_handler_info_t; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPDefs.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPDefs.h new file mode 100644 index 0000000000..378dd2fb1f --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPDefs.h @@ -0,0 +1,158 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _PPP_DEFS__H +#define _PPP_DEFS__H + +#include + + +// settings keys +#define PPP_DISONNECT_AFTER_IDLE_SINCE_KEY "DisonnectAfterIdleSince" +#define PPP_MODE_KEY "Mode" +#define PPP_DIAL_ON_DEMAND_KEY "DialOnDemand" +#define PPP_AUTO_REDIAL_KEY "AutoRedial" +#define PPP_LOAD_MODULE_KEY "LoadModule" +#define PPP_PROTOCOL_KEY "Protocol" +#define PPP_DEVICE_KEY "Device" +#define PPP_AUTHENTICATOR_KEY "Authenticator" +#define PPP_MULTILINK_KEY "Multilink-Protocol" + +// settings values +#define PPP_CLIENT_MODE_VALUE "Client" +#define PPP_SERVER_MODE_VALUE "Server" + +// path defines +#define PPP_MODULES_PATH "network/ppp" + +// built-in protocols +#define PPP_LCP_PROTOCOL 0xC021 + + +#define PPP_ERROR_BASE B_ERRORS_END + 1 + +// 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 (and PPPOptionHandler) + PPP_UNHANDLED = PPP_ERROR_BASE, + // The packet does not belong to this handler. + // Do not delete the packet when you return this! + + // 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 +}; + +// PFC options +enum { + PPP_REQUEST_PFC = 0x01, + // try to request PFC (does not fail if not successful) + PPP_ALLOW_PFC = 0x02, + // allow PFC if other side requests it + PPP_FORCE_PFC_REQUEST = 0x04, + // if PFC request fails the connection attempt will terminate + PPP_FREEZE_PFC_OPTIONS = 0x80 + // the options cannot be changed if this flag is set (mainly used by PPPDevice) +}; + +enum ppp_pfc_state { + PPP_PFC_DISABLED, + PPP_PFC_ACCEPTED, + PPP_PFC_REJECTED + // not used for peer state +}; + +// protocol and encapsulator flags +enum { + PPP_NO_FLAGS = 0x00, + PPP_ALWAYS_ALLOWED = 0x01, + // protocol may send/receive in Phase() >= PPP_ESTABLISHMENT_PHASE, + // but only LCP is allowed in State() != PPP_OPENED_STATE! + 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 = 0x04, + // if this protocol fails to go up we do not disconnect + PPP_INCLUDES_NCP = 0x08 + // This protocol includes the corresponding NCP protocol (e.g.: IPCP + IP). + // All protocol values will also be checked against Protocol() & 0x7FFF. +}; + +// phase when the protocol is brought up +enum ppp_phase { + // the following may be used by protocols + PPP_AUTHENTICATION_PHASE = 15, + PPP_NCP_PHASE = 20, + PPP_ESTABLISHED_PHASE = 25, + // only use PPP_ESTABLISHED_PHASE if + // you want to activate this protocol after + // the normal protocols like IP (i.e., IPCP) + + // the following must not be used by protocols! + PPP_DOWN_PHASE = 0, + PPP_TERMINATION_PHASE = 1, + // this is the selected phase when we are GOING down + PPP_ESTABLISHMENT_PHASE = 2 + // in this phase some protocols (with PPP_ALWAYS_ALLOWED + // flag set) may be used +}; + +// this defines the order in which the packets get encapsulated +enum ppp_encapsulation_level { + PPP_DEVICE_LEVEL = 0, + PPP_MULTILINK_LEVEL = 2, + PPP_ENCRYPTION_LEVEL = 5, + PPP_COMPRESSION_LEVEL = 10 +}; + +// we can be a ppp client or a ppp server interface +enum ppp_mode { + PPP_CLIENT_MODE = 0, + PPP_SERVER_MODE +}; + +// which side the protocol/encapsulator works for +enum ppp_side { + PPP_NO_SIDE, + PPP_LOCAL_SIDE, + PPP_PEER_SIDE, + PPP_BOTH_SIDES +}; + +// authentication status +enum ppp_authentication_status { + PPP_AUTHENTICATION_FAILED = -1, + PPP_NOT_AUTHENTICATED = 0, + PPP_AUTHENTICATION_SUCCESSFUL = 1, + PPP_AUTHENTICATING = 0xFF +}; + +// PPP states as defined in RFC 1661 +enum ppp_state { + PPP_INITIAL_STATE, + PPP_STARTING_STATE, + PPP_CLOSED_STATE, + PPP_STOPPED_STATE, + PPP_CLOSING_STATE, + PPP_STOPPING_STATE, + PPP_REQ_SENT_STATE, + PPP_ACK_RCVD_STATE, + PPP_ACK_SENT_STATE, + PPP_OPENED_STATE +}; + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPReportDefs.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPReportDefs.h new file mode 100644 index 0000000000..664b561947 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/headers/PPPReportDefs.h @@ -0,0 +1,67 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#ifndef _PPP_REPORT_DEFS__H +#define _PPP_REPORT_DEFS__H + + +#define PPP_REPORT_TIMEOUT 10 + +#define PPP_REPORT_DATA_LIMIT 128 + // how much optional data can be added to the report +#define PPP_REPORT_CODE '_3PR' + // the code of receive_data() must have this value + +// report flags +enum ppp_report_flags { + PPP_WAIT_FOR_REPLY = 0x1, + PPP_REMOVE_AFTER_REPORT = 0x2, + PPP_NO_REPLY_TIMEOUT = 0x4 +}; + +// report types +// the first 16 report types are reserved for the interface manager +enum ppp_report_type { + PPP_ALL_REPORTS = -1, + // used only when disabling reports + PPP_DESTRUCTION_REPORT = 16, + // the interface is being destroyed (no code is needed) + // this report is sent even if it was not requested + PPP_CONNECTION_REPORT = 17 +}; + +// report codes (type-specific) +enum ppp_connection_report_codes { + PPP_REPORT_GOING_UP = 0, + PPP_REPORT_UP_SUCCESSFUL = 1, + PPP_REPORT_DOWN_SUCCESSFUL = 2, + PPP_REPORT_UP_ABORTED = 3, + PPP_REPORT_DEVICE_UP_FAILED = 4, + PPP_REPORT_LOCAL_AUTHENTICATION_SUCCESSFUL = 5, + PPP_REPORT_PEER_AUTHENTICATION_SUCCESSFUL = 6, + PPP_REPORT_LOCAL_AUTHENTICATION_FAILED = 7, + PPP_REPORT_PEER_AUTHENTICATION_FAILED = 8, + PPP_REPORT_CONNECTION_LOST = 9 +}; + + +typedef struct ppp_report_packet { + int32 type; + int32 code; + uint8 length; + char data[PPP_REPORT_DATA_LIMIT]; +} ppp_report_packet; + + +typedef struct ppp_report_request { + ppp_report_type type; + thread_id thread; + int32 flags; +} ppp_report_request; + + +#endif diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.cpp b/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.cpp new file mode 100644 index 0000000000..c88e526412 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.cpp @@ -0,0 +1,148 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#include +#include + +#include "settings_tools.h" + +#include +#include + + +static void copy_parameter(const driver_parameter *from, driver_parameter *to); +static void free_driver_parameter(driver_parameter *p); + + +driver_settings* +dup_driver_settings(const driver_settings *dup) +{ + if(!dup) + return NULL; // we got a NULL pointer, so return nothing + + driver_settings *ret = (driver_settings*) malloc(sizeof(driver_settings)); + + ret->parameter_count = dup->parameter_count; + ret->parameters = (driver_parameter*) malloc(ret->parameter_count * sizeof(driver_parameter)); + + for(int32 i=0; i < ret->parameter_count; i++) + copy_parameter(&dup->parameters[i], &ret->parameters[i]); + + return ret; +} + + +static +void +copy_parameter(const driver_parameter *from, driver_parameter *to) +{ + to->name = strdup(from->name); + to->value_count = from->value_count; + + to->values = (char**) malloc(to->value_count * sizeof(char*)); + + for(int32 i=0; i < to->value_count; i++) + to->values[i] = strdup(from->values[i]); + + to->parameter_count = from->parameter_count; + + to->parameters = (driver_parameter*) malloc(to->parameter_count * sizeof(driver_parameter)); + + for(int32 i=0; i < to->parameter_count; i++) + copy_parameter(&from->parameters[i], &to->parameters[i]); +} + + +void +free_driver_settings(driver_settings *settings) +{ + for(int32 i=0; i < settings->parameter_count; i++) + free_driver_parameter(&settings->parameters[i]); + + free(settings->parameters); + free(settings); +} + + +void +free_driver_parameter(driver_parameter *p) +{ + free(p->name); + + for(int32 i=0; i < p->value_count; i++) + free(p->values[i]); + + free(p->values); + + for(int32 i=0; i < p->parameter_count; i++) + free_driver_parameter(&p->parameters[i]); + + free(p->parameters); +} + + +ppp_side +get_side_string_value(const char *sideString, ppp_side unknownValue) +{ + if(!sideString) + return unknownValue; + + if(!strcasecmp(sideString, "local")) + return PPP_LOCAL_SIDE; + if(!strcasecmp(sideString, "peer")) + return PPP_PEER_SIDE; + if(!strcasecmp(sideString, "none") + || !strcasecmp(sideString, "no")) + return PPP_NO_SIDE; + if(!strcasecmp(sideString, "both")) + return PPP_BOTH_SIDES; + + // no correct value has been found => return default value + return unknownValue; +} + + +bool +get_string_value(const char *string, bool unknownValue) +{ + if(!string) + return unknownValue; + + if (!strcmp(string, "1") + || !strcasecmp(string, "true") + || !strcasecmp(string, "yes") + || !strcasecmp(string, "on") + || !strcasecmp(string, "enable") + || !strcasecmp(string, "enabled")) + return true; + + if (!strcmp(string, "0") + || !strcasecmp(string, "false") + || !strcasecmp(string, "no") + || !strcasecmp(string, "off") + || !strcasecmp(string, "disable") + || !strcasecmp(string, "disabled")) + return false; + + // no correct value has been found => return default value + return unknownValue; +} + + +const char* +get_settings_value(const char *name, const driver_settings *settings) +{ + if(!name || !settings) + return NULL; + + for(int32 i=0; i < settings->parameter_count; i++) + if(!strcasecmp(settings->parameters[i].name, name) + && settings->parameters[i].value_count > 0) + return settings->parameters[i].values[0]; + + return NULL; +} diff --git a/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.h b/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.h new file mode 100644 index 0000000000..f45ac91c60 --- /dev/null +++ b/src/add-ons/kernel/network/ppp/shared/libkernelppp/settings_tools.h @@ -0,0 +1,27 @@ +//---------------------------------------------------------------------- +// This software is part of the OpenBeOS distribution and is covered +// by the OpenBeOS license. +// +// Copyright (c) 2003 Waldemar Kornewald, Waldemar.Kornewald@web.de +//--------------------------------------------------------------------- + +#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; + + +driver_settings *dup_driver_settings(const driver_settings *settings); +void free_driver_settings(driver_settings *settings); + + +ppp_side get_side_string_value(const char *sideString, ppp_side unknownValue); +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) + { return get_settings_value(name, (driver_settings*) ¶meters->parameter_count); } + +#endif