/* * Copyright 2003-2007, Waldemar Kornewald * Distributed under the terms of the MIT License. */ /*! \class KPPPInterface \brief The kernel representation of a PPP interface. This class is never created by the programmer directly. Instead, the PPP manager kernel module should be used. \n KPPPInterface handles all interface-specific commands from userspace and it passes packets to their receiver or sends them to the device. Additionally, it contains the KPPPLCP object with represents the LCP protocol and the KPPPStateMachine object which represents the state machine. \n All PPP modules are loaded from here. \n Protocols and encapsulators should be added to this class. LCP-specific extensions belong to the KPPPLCP object. \n Multilink support is distributed between KPPPInterface and KPPPStateMachine. */ // cstdio must be included before KPPPModule.h/KPPPManager.h because // dprintf is defined twice with different return values, once with // void (KernelExport.h) and once with int (stdio.h). #include #include #include #include #include #include #include // now our headers... #include // our other classes #include #include #include #include #include #include #include // general helper classes not only belonging to us // #include // #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 setting next event instead of receiving timer // events periodically // - add missing settings support (ConnectRetryDelay, etc.) //! Private structure needed for reconnecting. typedef struct reconnect_info { KPPPInterface *interface; thread_id *thread; uint32 delay; } reconnect_info; extern net_buffer_module_info *gBufferModule; extern net_stack_module_info *gStackModule; status_t reconnect_thread(void *data); // other functions status_t interface_deleter_thread(void *data); /*! \brief Creates a new interface. \param name Name of the PPP interface description file. \param entry The PPP manager passes an internal structure to the constructor. \param ID The interface's ID. \param settings (Optional): If no name is given you must pass the settings here. \param parent (Optional): Interface's parent (only used for multilink interfaces). */ KPPPInterface::KPPPInterface(const char *name, ppp_interface_entry *entry, ppp_interface_id ID, const driver_settings *settings, KPPPInterface *parent) : KPPPLayer(name, PPP_INTERFACE_LEVEL, 2), fID(ID), fSettings(NULL), fIfnet(NULL), fReconnectThread(-1), fConnectAttempt(1), fConnectRetriesLimit(0), fManager(NULL), fConnectedSince(0), fIdleSince(0), fMRU(1500), fInterfaceMTU(1498), fHeaderLength(2), fParent(NULL), fIsMultilink(false), fAutoReconnect(false), fConnectOnDemand(true), fAskBeforeConnecting(false), fMode(PPP_CLIENT_MODE), fLocalPFCState(PPP_PFC_DISABLED), fPeerPFCState(PPP_PFC_DISABLED), fPFCOptions(0), fDevice(NULL), fFirstProtocol(NULL), fStateMachine(*this), fLCP(*this), fReportManager(StateMachine().fLock), fLock(StateMachine().fLock), fDeleteCounter(0) { entry->interface = this; if (name) { // load settings from description file char path[B_PATH_NAME_LENGTH]; sprintf(path, "ptpnet/%s", name); void *handle = load_driver_settings(path); if (!handle) { ERROR("KPPPInterface: Unable to load %s PPP driver settings!\n", path); fInitStatus = B_ERROR; return; } fSettings = dup_driver_settings(get_driver_settings(handle)); unload_driver_settings(handle); } else fSettings = dup_driver_settings(settings); // use the given settings if (!fSettings) { ERROR("KPPPInterface: No fSettings!\n"); fInitStatus = B_ERROR; return; } // add internal modules // LCP if (!AddProtocol(&LCP())) { ERROR("KPPPInterface: Could not add LCP protocol!\n"); fInitStatus = B_ERROR; return; } // MRU _KPPPMRUHandler *mruHandler = new _KPPPMRUHandler(*this); if (!LCP().AddOptionHandler(mruHandler) || mruHandler->InitCheck() != B_OK) { ERROR("KPPPInterface: Could not add MRU handler!\n"); delete mruHandler; } // authentication _KPPPAuthenticationHandler *authenticationHandler = new _KPPPAuthenticationHandler(*this); if (!LCP().AddOptionHandler(authenticationHandler) || authenticationHandler->InitCheck() != B_OK) { ERROR("KPPPInterface: Could not add authentication handler!\n"); delete authenticationHandler; } // PFC _KPPPPFCHandler *pfcHandler = new _KPPPPFCHandler(fLocalPFCState, fPeerPFCState, *this); if (!LCP().AddOptionHandler(pfcHandler) || pfcHandler->InitCheck() != B_OK) { ERROR("KPPPInterface: Could not add PFC handler!\n"); delete pfcHandler; } // set up connect delays fConnectRetryDelay = 3000; // 3s delay between each new attempt to reconnect fReconnectDelay = 1000; // 1s delay between lost connection and reconnect if (get_module(PPP_INTERFACE_MODULE_NAME, (module_info**) &fManager) != B_OK) ERROR("KPPPInterface: Manager module not found!\n"); // are we a multilink subinterface? if (parent && parent->IsMultilink()) { fParent = parent; fParent->AddChild(this); fIsMultilink = true; } RegisterInterface(); if (!fSettings) { fInitStatus = B_ERROR; return; } const char *value; // get login value = get_settings_value(PPP_USERNAME_KEY, fSettings); fUsername = value ? strdup(value) : strdup(""); value = get_settings_value(PPP_PASSWORD_KEY, fSettings); fPassword = value ? strdup(value) : strdup(""); // 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 (value && !strcasecmp(value, PPP_SERVER_MODE_VALUE)) fMode = PPP_SERVER_MODE; else fMode = PPP_CLIENT_MODE; // we are a client by default SetAutoReconnect( get_boolean_value( get_settings_value(PPP_AUTO_RECONNECT_KEY, fSettings), false) ); // auto reconnect is disabled by default fAskBeforeConnecting = get_boolean_value( get_settings_value(PPP_ASK_BEFORE_CONNECTING_KEY, fSettings), false); // load all protocols and the device if (!LoadModules(fSettings, 0, fSettings->parameter_count)) { ERROR("KPPPInterface: Error loading modules!\n"); fInitStatus = B_ERROR; } } //! Destructor: Disconnects and marks interface for deletion. KPPPInterface::~KPPPInterface() { TRACE("KPPPInterface: Destructor\n"); // tell protocols to uninit (remove routes, etc.) KPPPProtocol *protocol = FirstProtocol(); for (; protocol; protocol = protocol->NextProtocol()) protocol->Uninit(); // make sure we are not accessible by any thread before we continue UnregisterInterface(); if (fManager) fManager->RemoveInterface(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(); { MutexLocker locker(fLock); if (State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) break; } } Report(PPP_DESTRUCTION_REPORT, 0, &fID, sizeof(ppp_interface_id)); // tell all listeners that we are being destroyed int32 tmp; send_data_with_timeout(fReconnectThread, 0, NULL, 0, 200); // tell thread that we are being destroyed (200ms timeout) wait_for_thread(fReconnectThread, &tmp); while (CountChildren()) delete ChildAt(0); delete Device(); while (FirstProtocol()) { if (FirstProtocol() == &LCP()) fFirstProtocol = fFirstProtocol->NextProtocol(); else delete FirstProtocol(); // destructor removes protocol from list } for (int32 index = 0; index < fModules.CountItems(); index++) { put_module(fModules.ItemAt(index)); delete[] fModules.ItemAt(index); } free_driver_settings(fSettings); if (Parent()) Parent()->RemoveChild(this); if (fManager) put_module(PPP_INTERFACE_MODULE_NAME); } //! Marks interface for deletion. void KPPPInterface::Delete() { // MutexLocker locker(fLock); // alreay locked in KPPPStatemachine::DownEvent // uncomment this line will cause double lock if (fDeleteCounter > 0) return; // only one thread should delete us! fDeleteCounter = 1; fManager->DeleteInterface(ID()); // This will mark us for deletion. // Any subsequent calls to delete_interface() will do nothing. } //! Returns if interface was initialized correctly. status_t KPPPInterface::InitCheck() const { if (fInitStatus != B_OK) return fInitStatus; if (!fSettings || !fManager) 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; } //! The username used for authentication. const char* KPPPInterface::Username() const { // this data is not available before we authenticate if (Phase() < PPP_AUTHENTICATION_PHASE) return NULL; return fUsername; } //! The password used for authentication. const char* KPPPInterface::Password() const { // this data is not available before we authenticate if (Phase() < PPP_AUTHENTICATION_PHASE) return NULL; return fPassword; } //! Sets interface MRU. bool KPPPInterface::SetMRU(uint32 MRU) { TRACE("KPPPInterface: SetMRU(%ld)\n", MRU); if (Device() && MRU > Device()->MTU() - 2) return false; // MutexLocker locker(fLock); // uncomment this line will cause double lock // alreay locked in ::Up and ::KPPPInterface fMRU = MRU; CalculateInterfaceMTU(); return true; } //! Returns number of bytes spent for protocol overhead. Includes device overhead. uint32 KPPPInterface::PacketOverhead() const { uint32 overhead = fHeaderLength + 2; if (Device()) overhead += Device()->Overhead(); return overhead; } /*! \brief Allows accessing additional functions. This is normally called by userland apps to get information about the interface. \param op The op value (see ppp_control_ops enum). \param data (Optional): Additional data may be needed for this op. \param length Length of data. \return - \c B_OK: \c Control() was successful. - \c B_ERROR: Either \a length is too small or data is NULL. - \c B_NOT_ALLOWED: Operation not allowed (at this point in time). - \c B_BAD_INDEX: Wrong index (e.g.: when accessing interface submodules). - \c B_BAD_VALUE: Unknown op. - Return value of submodule (when controlling one). */ status_t KPPPInterface::Control(uint32 op, void *data, size_t length) { TRACE("%s:%s\n", __FILE__, __func__); control_net_module_args* args = (control_net_module_args*)data; if (op != NET_STACK_CONTROL_NET_MODULE) { dprintf("unknow op!!\n"); return B_BAD_VALUE; } switch (args->op) { case PPPC_COUNT_INTERFACES: { // should be implepented dprintf("PPPC_COUNT_INTERFACES should be implepentd\n"); return B_OK; } case PPPC_GET_INTERFACES: { dprintf("PPPC_GET_INTERFACES\n"); ppp_get_interfaces_info* info = (ppp_get_interfaces_info*)args->data; dprintf("info->interfaces: %p\n", info->interfaces); *(info->interfaces) = 1; info->resultCount = 1; return B_OK; } case PPPC_CONTROL_INTERFACE: { dprintf("PPPC_CONTROL_INTERFACE\n"); ppp_control_info* control = (ppp_control_info*)args->data; switch (control->op) { case PPPC_GET_INTERFACE_INFO: { dprintf("PPPC_GET_INTERFACE_INFO\n"); if (control->length < sizeof(ppp_interface_info_t) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } ppp_interface_info *info = (ppp_interface_info*) control->data; dprintf("info addr:%p\n", info); memset(info, 0, sizeof(ppp_interface_info_t)); if (Name()) strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); if (Ifnet()) info->if_unit = Ifnet()->index; else info->if_unit = -1; 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->optionHandlersCount = LCP().CountOptionHandlers(); info->LCPExtensionsCount = 0; info->childrenCount = CountChildren(); info->MRU = MRU(); info->interfaceMTU = InterfaceMTU(); info->connectAttempt = fConnectAttempt; info->connectRetriesLimit = fConnectRetriesLimit; info->connectRetryDelay = ConnectRetryDelay(); info->reconnectDelay = ReconnectDelay(); info->connectedSince = ConnectedSince(); info->idleSince = IdleSince(); info->disconnectAfterIdleSince = DisconnectAfterIdleSince(); info->doesConnectOnDemand = DoesConnectOnDemand(); info->doesAutoReconnect = DoesAutoReconnect(); info->hasDevice = Device(); info->isMultilink = IsMultilink(); info->hasParent = Parent(); break; } case PPPC_SET_USERNAME: { dprintf("PPPC_SET_USERNAME\n"); if (control->length > PPP_HANDLER_NAME_LENGTH_LIMIT || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } MutexLocker locker(fLock); // login information can only be changed before we authenticate if (Phase() >= PPP_AUTHENTICATION_PHASE) return B_NOT_ALLOWED; free(fUsername); fUsername = control->data ? strdup((const char*) control->data) : strdup(""); dprintf("set ppp user name to %s\n", fUsername); break; } case PPPC_SET_PASSWORD: { dprintf("PPPC_SET_PASSWORD\n"); if (control->length > PPP_HANDLER_NAME_LENGTH_LIMIT || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } MutexLocker locker(fLock); // login information can only be changed before we authenticate if (Phase() >= PPP_AUTHENTICATION_PHASE) return B_NOT_ALLOWED; free(fPassword); fPassword = control->data ? strdup((const char*) control->data) : strdup(""); dprintf("set ppp password to %s!\n", fPassword); break; } case PPPC_SET_ASK_BEFORE_CONNECTING: { dprintf("PPPC_SET_ASK_BEFORE_CONNECTING\n"); if (control->length < sizeof(uint32) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } SetAskBeforeConnecting(*((uint32*)control->data)); dprintf("goto PPPC_SET_ASK_BEFORE_CONNECTING here!\n"); break; } case PPPC_GET_STATISTICS: { dprintf("PPPC_GET_STATISTICS\n"); if (control->length < sizeof(ppp_statistics) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } dprintf("should PPPC_GET_STATISTICS here!\n"); memcpy(control->data, &fStatistics, sizeof(ppp_statistics)); break; } case PPPC_HAS_INTERFACE_SETTINGS: { dprintf("PPPC_HAS_INTERFACE_SETTINGS\n"); if (control->length < sizeof(driver_settings) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } dprintf("should PPPC_HAS_INTERFACE_SETTINGS here!\n"); if (equal_interface_settings(Settings(), (driver_settings*)control->data)) return B_OK; else return B_ERROR; break; } case PPPC_ENABLE_REPORTS: { dprintf("PPPC_ENABLE_REPORTS\n"); if (control->length < sizeof(ppp_report_request) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } dprintf("should PPPC_ENABLE_REPORTS here!\n"); MutexLocker locker(fLock); ppp_report_request *request = (ppp_report_request*) control->data; // first, we send an initial state report if (request->type == PPP_CONNECTION_REPORT) { ppp_report_packet report; report.type = PPP_CONNECTION_REPORT; report.code = StateMachine().fLastConnectionReportCode; report.length = sizeof(fID); KPPPReportManager::SendReport(request->thread, &report); if (request->flags & PPP_REMOVE_AFTER_REPORT) return B_OK; } ReportManager().EnableReports(request->type, request->thread, request->flags); break; } case PPPC_DISABLE_REPORTS: { dprintf("PPPC_DISABLE_REPORTS\n"); if (control->length < sizeof(ppp_report_request) || !control->data) { dprintf("size wrong!\n"); return B_ERROR; } dprintf("should PPPC_DISABLE_REPORTS here!\n"); ppp_report_request *request = (ppp_report_request*) control->data; ReportManager().DisableReports(request->type, request->thread); break; } case PPPC_CONTROL_DEVICE: { dprintf("PPPC_CONTROL_DEVICE\n"); if (control->length < sizeof(ppp_control_info) || !control->data) return B_ERROR; ppp_control_info *controlInfo = (ppp_control_info*) control->data; if (controlInfo->index != 0 || !Device()) { dprintf("index is 0 or no Device\n"); return B_BAD_INDEX; } return Device()->Control(controlInfo->op, controlInfo->data, controlInfo->length); } case PPPC_CONTROL_PROTOCOL: { dprintf("PPPC_CONTROL_PROTOCOL\n"); if (control->length < sizeof(ppp_control_info) || !control->data) return B_ERROR; ppp_control_info *controlInfo = (ppp_control_info*) control->data; KPPPProtocol *protocol = ProtocolAt(controlInfo->index); if (!protocol) return B_BAD_INDEX; return protocol->Control(controlInfo->op, controlInfo->data, controlInfo->length); } case PPPC_CONTROL_OPTION_HANDLER: { dprintf("PPPC_CONTROL_OPTION_HANDLER\n"); if (control->length < sizeof(ppp_control_info) || !control->data) return B_ERROR; ppp_control_info *controlInfo = (ppp_control_info*) control->data; KPPPOptionHandler *optionHandler = LCP().OptionHandlerAt(controlInfo->index); if (!optionHandler) { dprintf("optionHandler no avail\n"); return B_BAD_INDEX; } return optionHandler->Control(controlInfo->op, controlInfo->data, controlInfo->length); } case PPPC_CONTROL_LCP_EXTENSION: { dprintf("PPPC_CONTROL_LCP_EXTENSION\n"); if (control->length < sizeof(ppp_control_info) || !control->data) return B_ERROR; ppp_control_info *controlInfo = (ppp_control_info*) control->data; KPPPLCPExtension *lcpExtension = LCP().LCPExtensionAt(controlInfo->index); if (!lcpExtension) return B_BAD_INDEX; return lcpExtension->Control(controlInfo->op, controlInfo->data, controlInfo->length); } case PPPC_CONTROL_CHILD: { dprintf("PPPC_CONTROL_CHILD\n"); if (control->length < sizeof(ppp_control_info) || !control->data) return B_ERROR; ppp_control_info *controlInfo = (ppp_control_info*) control->data; KPPPInterface *child = ChildAt(controlInfo->index); if (!child) return B_BAD_INDEX; return child->Control(controlInfo->op, controlInfo->data, controlInfo->length); } default : return B_ERROR; } return B_OK; } case PPPC_GET_INTERFACE_INFO: { if (length < sizeof(ppp_interface_info_t) || !data) return B_ERROR; ppp_interface_info *info = (ppp_interface_info*) data; memset(info, 0, sizeof(ppp_interface_info_t)); if (Name()) strncpy(info->name, Name(), PPP_HANDLER_NAME_LENGTH_LIMIT); if (Ifnet()) info->if_unit = Ifnet()->index; else info->if_unit = -1; 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->optionHandlersCount = LCP().CountOptionHandlers(); info->LCPExtensionsCount = 0; info->childrenCount = CountChildren(); info->MRU = MRU(); info->interfaceMTU = InterfaceMTU(); info->connectAttempt = fConnectAttempt; info->connectRetriesLimit = fConnectRetriesLimit; info->connectRetryDelay = ConnectRetryDelay(); info->reconnectDelay = ReconnectDelay(); info->connectedSince = ConnectedSince(); info->idleSince = IdleSince(); info->disconnectAfterIdleSince = DisconnectAfterIdleSince(); info->doesConnectOnDemand = DoesConnectOnDemand(); info->doesAutoReconnect = DoesAutoReconnect(); info->hasDevice = Device(); info->isMultilink = IsMultilink(); info->hasParent = Parent(); break; } case PPPC_SET_USERNAME: { if (!data) return B_ERROR; MutexLocker locker(fLock); // login information can only be changed before we authenticate if (Phase() >= PPP_AUTHENTICATION_PHASE) return B_NOT_ALLOWED; free(fUsername); fUsername = data ? strdup((const char*) data) : strdup(""); break; } case PPPC_SET_PASSWORD: { if (!data) return B_ERROR; MutexLocker locker(fLock); // login information can only be changed before we authenticate if (Phase() >= PPP_AUTHENTICATION_PHASE) return B_NOT_ALLOWED; free(fPassword); fPassword = data ? strdup((const char*) data) : strdup(""); break; } case PPPC_SET_ASK_BEFORE_CONNECTING: if (length < sizeof(uint32) || !data) return B_ERROR; SetAskBeforeConnecting(*((uint32*)data)); break; case PPPC_SET_MRU: if (length < sizeof(uint32) || !data) return B_ERROR; SetMRU(*((uint32*)data)); break; case PPPC_SET_CONNECT_ON_DEMAND: if (length < sizeof(uint32) || !data) return B_ERROR; SetConnectOnDemand(*((uint32*)data)); break; case PPPC_SET_AUTO_RECONNECT: if (length < sizeof(uint32) || !data) return B_ERROR; SetAutoReconnect(*((uint32*)data)); break; case PPPC_HAS_INTERFACE_SETTINGS: if (length < sizeof(driver_settings) || !data) return B_ERROR; if (equal_interface_settings(Settings(), (driver_settings*) data)) return B_OK; else return B_ERROR; break; case PPPC_ENABLE_REPORTS: { if (length < sizeof(ppp_report_request) || !data) return B_ERROR; MutexLocker locker(fLock); ppp_report_request *request = (ppp_report_request*) data; // first, we send an initial state report if (request->type == PPP_CONNECTION_REPORT) { ppp_report_packet report; report.type = PPP_CONNECTION_REPORT; report.code = StateMachine().fLastConnectionReportCode; report.length = sizeof(fID); KPPPReportManager::SendReport(request->thread, &report); if (request->flags & PPP_REMOVE_AFTER_REPORT) return B_OK; } ReportManager().EnableReports(request->type, request->thread, request->flags); break; } case PPPC_DISABLE_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_GET_STATISTICS: if (length < sizeof(ppp_statistics) || !data) return B_ERROR; memcpy(data, &fStatistics, sizeof(ppp_statistics)); 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); } case PPPC_CONTROL_PROTOCOL: { if (length < sizeof(ppp_control_info) || !data) return B_ERROR; ppp_control_info *control = (ppp_control_info*) data; KPPPProtocol *protocol = ProtocolAt(control->index); if (!protocol) return B_BAD_INDEX; return protocol->Control(control->op, control->data, control->length); } case PPPC_CONTROL_OPTION_HANDLER: { if (length < sizeof(ppp_control_info) || !data) return B_ERROR; ppp_control_info *control = (ppp_control_info*) data; KPPPOptionHandler *optionHandler = LCP().OptionHandlerAt(control->index); if (!optionHandler) return B_BAD_INDEX; return optionHandler->Control(control->op, control->data, control->length); } case PPPC_CONTROL_LCP_EXTENSION: { if (length < sizeof(ppp_control_info) || !data) return B_ERROR; ppp_control_info *control = (ppp_control_info*) data; KPPPLCPExtension *lcpExtension = LCP().LCPExtensionAt(control->index); if (!lcpExtension) return B_BAD_INDEX; return lcpExtension->Control(control->op, control->data, control->length); } case PPPC_CONTROL_CHILD: { if (length < sizeof(ppp_control_info) || !data) return B_ERROR; ppp_control_info *control = (ppp_control_info*) data; KPPPInterface *child = ChildAt(control->index); if (!child) return B_BAD_INDEX; return child->Control(control->op, control->data, control->length); } default: dprintf("bad ppp_interface_control!\n"); return B_BAD_VALUE; } return B_OK; } /*! \brief Sets a new device for this interface. A device add-on should call this method to register itself. The best place to do this is in your module's \c add_to() function. \param device The device object. \return \c true if successful or \c false otherwise. \sa KPPPDevice \sa kppp_module_info */ bool KPPPInterface::SetDevice(KPPPDevice *device) { TRACE("KPPPInterface: SetDevice(%p)\n", device); if (device && &device->Interface() != this) return false; if (IsMultilink() && !Parent()) return false; // main interfaces do not have devices MutexLocker locker(fLock); if (Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change if (fDevice && (IsUp() || fDevice->IsUp())) Down(); fDevice = device; SetNext(device); if (fDevice) fMRU = fDevice->MTU() - 2; CalculateInterfaceMTU(); CalculateBaudRate(); return true; } /*! \brief Adds a new protocol to this interface. NOTE: You can only add protocols in \c PPP_DOWN_PHASE. \n A protocol add-on should call this method to register itself. The best place to do this is in your module's \c add_to() function. \param protocol The protocol object. \return \c true if successful or \c false otherwise. \sa KPPPProtocol \sa kppp_module_info */ bool KPPPInterface::AddProtocol(KPPPProtocol *protocol) { // Find insert position after the last protocol // with the same level. TRACE("KPPPInterface: AddProtocol(%X)\n", protocol ? protocol->ProtocolNumber() : 0); if (!protocol || &protocol->Interface() != this || protocol->Level() == PPP_INTERFACE_LEVEL) return false; MutexLocker locker(fLock); if (Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change KPPPProtocol *current = fFirstProtocol, *previous = NULL; while (current) { if (current->Level() < protocol->Level()) break; previous = current; current = current->NextProtocol(); } if (!current) { if (!previous) fFirstProtocol = protocol; else previous->SetNextProtocol(protocol); // set up the last protocol in the chain protocol->SetNextProtocol(NULL); // this also sets next to NULL protocol->SetNext(this); // we need to set us as the next layer for the last protocol } else { protocol->SetNextProtocol(current); if (!previous) fFirstProtocol = protocol; else previous->SetNextProtocol(protocol); } if (protocol->Level() < PPP_PROTOCOL_LEVEL) CalculateInterfaceMTU(); if (IsUp() || Phase() >= protocol->ActivationPhase()) protocol->Up(); return true; } /*! \brief Removes a protocol from this interface. NOTE: You can only remove protocols in \c PPP_DOWN_PHASE. \n A protocol add-on should call this method to remove itself explicitly from the interface. \n Normally, this method is called in KPPPProtocol's destructor. Do not call it yourself unless you know what you do! \param protocol The protocol object. \return \c true if successful or \c false otherwise. */ bool KPPPInterface::RemoveProtocol(KPPPProtocol *protocol) { TRACE("KPPPInterface: RemoveProtocol(%X)\n", protocol ? protocol->ProtocolNumber() : 0); MutexLocker locker(fLock); if (Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change KPPPProtocol *current = fFirstProtocol, *previous = NULL; while (current) { if (current == protocol) { if (!protocol->IsDown()) protocol->Down(); if (previous) { previous->SetNextProtocol(current->NextProtocol()); // set us as next layer if needed if (!previous->Next()) previous->SetNext(this); } else fFirstProtocol = current->NextProtocol(); current->SetNextProtocol(NULL); CalculateInterfaceMTU(); return true; } previous = current; current = current->NextProtocol(); } return false; } //! Returns the number of protocol modules belonging to this interface. int32 KPPPInterface::CountProtocols() const { MutexLocker locker(fLock); KPPPProtocol *protocol = FirstProtocol(); int32 count = 0; for (; protocol; protocol = protocol->NextProtocol()) ++count; return count; } //! Returns the protocol at the given \a index or \c NULL if it could not be found. KPPPProtocol* KPPPInterface::ProtocolAt(int32 index) const { MutexLocker locker(fLock); KPPPProtocol *protocol = FirstProtocol(); int32 currentIndex = 0; for (; protocol && currentIndex != index; protocol = protocol->NextProtocol()) ++currentIndex; return protocol; } /*! \brief Returns the protocol object responsible for a given protocol number. \param protocolNumber The protocol number that the object should handle. \param start (Optional): Start with this protocol. Can be used for iteration. \return Either the object that was found or \c NULL. */ KPPPProtocol* KPPPInterface::ProtocolFor(uint16 protocolNumber, KPPPProtocol *start) const { TRACE("KPPPInterface: ProtocolFor(%X)\n", protocolNumber); // MutexLocker locker(fLock); // already locked in ::Receive, uncomment this line will cause double lock KPPPProtocol *current = start ? start : FirstProtocol(); for (; current; current = current->NextProtocol()) { if (current->ProtocolNumber() == protocolNumber || (current->Flags() & PPP_INCLUDES_NCP && (current->ProtocolNumber() & 0x7FFF) == (protocolNumber & 0x7FFF))) return current; } return NULL; } //! Adds a new child interface (used for multilink interfaces). bool KPPPInterface::AddChild(KPPPInterface *child) { TRACE("KPPPInterface: AddChild(%lX)\n", child ? child->ID() : 0); if (!child) return false; MutexLocker locker(fLock); if (fChildren.HasItem(child) || !fChildren.AddItem(child)) return false; child->SetParent(this); return true; } //! Removes a new child from this interface (used for multilink interfaces). bool KPPPInterface::RemoveChild(KPPPInterface *child) { TRACE("KPPPInterface: RemoveChild(%lX)\n", child ? child->ID() : 0); MutexLocker 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; } //! Returns the child interface at the given \a index (used for multilink interfaces). KPPPInterface* KPPPInterface::ChildAt(int32 index) const { TRACE("KPPPInterface: ChildAt(%ld)\n", index); MutexLocker locker(fLock); KPPPInterface *child = fChildren.ItemAt(index); if (child == fChildren.GetDefaultItem()) return NULL; return child; } //! Enables or disables the auto-reconnect feture. void KPPPInterface::SetAutoReconnect(bool autoReconnect) { TRACE("KPPPInterface: SetAutoReconnect(%s)\n", autoReconnect ? "true" : "false"); if (Mode() != PPP_CLIENT_MODE) return; fAutoReconnect = autoReconnect; } //! Enables or disables the connect-on-demand feature. void KPPPInterface::SetConnectOnDemand(bool connectOnDemand) { // All protocols must check if ConnectOnDemand was enabled/disabled after this // interface went down. This is the only situation where a change is relevant. TRACE("KPPPInterface: SetConnectOnDemand(%s)\n", connectOnDemand ? "true" : "false"); MutexLocker locker(fLock); // Only clients support ConnectOnDemand. if (Mode() != PPP_CLIENT_MODE) { TRACE("KPPPInterface::SetConnectOnDemand(): Wrong mode!\n"); fConnectOnDemand = false; return; } else if (DoesConnectOnDemand() == connectOnDemand) return; fConnectOnDemand = connectOnDemand; // 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 (!connectOnDemand) Delete(); // as long as the protocols were not configured we can just delete us return; } // check if we need to set/unset flags if (connectOnDemand) { if (Ifnet()) Ifnet()->flags |= IFF_UP; } else if (!connectOnDemand && Phase() < PPP_ESTABLISHED_PHASE) { if (Ifnet()) Ifnet()->flags &= ~IFF_UP; } } //! Sets whether the user is asked before establishing the connection. void KPPPInterface::SetAskBeforeConnecting(bool ask) { MutexLocker locker(fLock); bool old = fAskBeforeConnecting; fAskBeforeConnecting = ask; if (old && fAskBeforeConnecting == false && State() == PPP_STARTING_STATE && Phase() == PPP_DOWN_PHASE) { // locker.Unlock(); StateMachine().ContinueOpenEvent(); } } //! Sets Protocol-Field-Compression options. bool KPPPInterface::SetPFCOptions(uint8 pfcOptions) { TRACE("KPPPInterface: SetPFCOptions(0x%X)\n", pfcOptions); MutexLocker locker(fLock); if (PFCOptions() & PPP_FREEZE_PFC_OPTIONS) return false; fPFCOptions = pfcOptions; return true; } /*! \brief Brings this interface up. \c Down() overrides all \c Up() requests. \n This method runs an asynchronous process (it returns immediately). \return \c false on error. */ bool KPPPInterface::Up() { TRACE("KPPPInterface: Up()\n"); if (InitCheck() != B_OK || Phase() == PPP_TERMINATION_PHASE) return false; if (IsUp()) return true; MutexLocker locker(fLock); StateMachine().OpenEvent(); return true; } /*! \brief Brings this interface down. \c Down() overrides all \c Up() requests. \n This method runs an asynchronous process (it returns immediately). \return \c false on error. */ bool KPPPInterface::Down() { TRACE("KPPPInterface: Down()\n"); if (InitCheck() != B_OK) return false; else if (State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) return true; send_data_with_timeout(fReconnectThread, 0, NULL, 0, 200); // tell the reconnect thread to abort its attempt (if it's still waiting) MutexLocker locker(fLock); StateMachine().CloseEvent(); return true; } //! Waits for connection establishment. Returns true if successful. bool KPPPInterface::WaitForConnection() { TRACE("KPPPInterface: WaitForConnection()\n"); if (InitCheck() != B_OK) return false; // just delay ~3 seconds to wait for ppp go up for (uint32 i = 0; i < 10000; i++) for (uint32 j = 0; j < 3000000; j++) return true; // for temporary ReportManager().EnableReports(PPP_CONNECTION_REPORT, find_thread(NULL)); ppp_report_packet report; thread_id sender; bool successful = false; while (true) { if (receive_data(&sender, &report, sizeof(report)) != PPP_REPORT_CODE) continue; if (report.type == PPP_DESTRUCTION_REPORT) break; else if (report.type != PPP_CONNECTION_REPORT) continue; if (report.code == PPP_REPORT_UP_SUCCESSFUL) { successful = true; break; } else if (report.code == PPP_REPORT_DOWN_SUCCESSFUL) break; } ReportManager().DisableReports(PPP_CONNECTION_REPORT, find_thread(NULL)); dprintf("KPPPInterface: WaitForConnection():%s\n", successful ? "True" : "False"); return successful; } /*! \brief Loads modules specified in the settings structure. \param settings PPP interface description file format settings. \param start Index of driver_parameter to start with. \param count Number of driver_parameters to look at. \return \c true if successful or \c false otherwise. */ bool KPPPInterface::LoadModules(driver_settings *settings, int32 start, int32 count) { TRACE("KPPPInterface: LoadModules()\n"); 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], 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->CreateInterface(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], type)) return false; } return true; } /*! \brief Loads a specific module. \param name Name of the module. \param parameter Module settings. \param type Type of module. \return \c true if successful or \c false otherwise. */ bool KPPPInterface::LoadModule(const char *name, driver_parameter *parameter, ppp_module_key_type type) { TRACE("KPPPInterface: LoadModule(%s)\n", name ? name : "XXX: NO NAME"); if (Phase() != PPP_DOWN_PHASE) return false; // a running connection may not change if (!name || strlen(name) > B_FILE_NAME_LENGTH) return false; char *moduleName = new char[B_PATH_NAME_LENGTH]; sprintf(moduleName, "%s/%s", PPP_MODULES_PATH, name); ppp_module_info *module; if (get_module(moduleName, (module_info**) &module) != B_OK) { delete[] moduleName; return false; } // add the module to the list of loaded modules // for putting them on our destruction fModules.AddItem(moduleName); return module->add_to(Parent() ? *Parent() : *this, this, parameter, type); } //! Always returns true. bool KPPPInterface::IsAllowedToSend() const { return true; } /*! \brief Sends a packet to the device. This brings the interface up if connect-on-demand is enabled and we are not connected. \n PFC encoding is handled here. \n NOTE: In order to prevent interface destruction while sending you must either hold a refcount for this interface or make sure it is locked. \param packet The packet. \param protocolNumber The packet's protocol number. \return - \c B_OK: Sending was successful. - \c B_ERROR: Some error occured. */ status_t KPPPInterface::Send(net_buffer *packet, uint16 protocolNumber) { TRACE("KPPPInterface: Send(0x%X)\n", protocolNumber); 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) { ERROR("InitCheck() fail\n"); gBufferModule->free(packet); return B_ERROR; } // go up if ConnectOnDemand is enabled and we are disconnected // TODO: our new netstack will simplify ConnectOnDemand handling, so // we do not have to handle it here if ((protocolNumber != PPP_LCP_PROTOCOL && DoesConnectOnDemand() && (Phase() == PPP_DOWN_PHASE || Phase() == PPP_ESTABLISHMENT_PHASE) && !Up()) && !WaitForConnection()) { dprintf("DoesConnectOnDemand fail!\n"); // gBufferModule->free(packet); return B_ERROR; } // find the protocol handler for the current protocol number KPPPProtocol *protocol = ProtocolFor(protocolNumber); while (protocol && !protocol->IsEnabled()) protocol = protocol->NextProtocol() ? ProtocolFor(protocolNumber, protocol->NextProtocol()) : NULL; #if DEBUG if (!protocol) TRACE("KPPPInterface::Send(): no protocol found!\n"); else if (!Device()->IsUp()) TRACE("KPPPInterface::Send(): device is not up!\n"); else if (!protocol->IsEnabled()) TRACE("KPPPInterface::Send(): protocol not enabled!\n"); else if (!IsProtocolAllowed(*protocol)) TRACE("KPPPInterface::Send(): protocol not allowed to send!\n"); else TRACE("KPPPInterface::Send(): protocol allowed\n"); #endif // make sure that protocol is allowed to send and everything is up if (Device()->IsUp() && protocolNumber == 0x0021) { // IP_PROTOCOL 0x0021 TRACE("send IP packet!\n"); } else if (!Device()->IsUp() || !protocol || !protocol->IsEnabled() || !IsProtocolAllowed(*protocol)) { ERROR("KPPPInterface::Send(): Device is down, throw packet away!!\n"); // gBufferModule->free(packet); return B_ERROR; } // encode in ppp frame and consider using PFC if (UseLocalPFC() && (protocolNumber & 0xFF00) == 0) { TRACE("%s::%s should not go here\n", __FILE__, __func__); NetBufferPrepend bufferHeader(packet); if (bufferHeader.Status() != B_OK) return bufferHeader.Status(); uint8 &header = bufferHeader.Data(); // memcpy(header.destination, ether_pppoe_ppp_header, 1); header = (protocolNumber & 0x00FF); // bufferHeader.Sync(); } else { NetBufferPrepend bufferHeader(packet); if (bufferHeader.Status() != B_OK) return bufferHeader.Status(); uint16 &header = bufferHeader.Data(); // set protocol (the only header field) header = htons(protocolNumber); // bufferHeader.Sync(); } // pass to device if we're either not a multilink interface or a child interface if (!IsMultilink() || Parent()) { // check if packet is too big for device uint32 length = packet->size; if (length > MRU()) { dprintf("packet too large!\n"); gBufferModule->free(packet); return B_ERROR; } atomic_add64(&fStatistics.bytesSent, length); atomic_add64(&fStatistics.packetsSent, 1); TRACE("%s::%s SendToNext\n", __FILE__, __func__); return SendToNext(packet, 0); // this is normally the device, but there can be something inbetween } else { // the multilink protocol should have sent it to some child interface TRACE("It is weird to go here!\n"); gBufferModule->free(packet); return B_ERROR; } } /*! \brief Receives a packet. Encapsulation protocols may use this method to pass encapsulated packets to the PPP interface. Packets will be handled as if they were raw packets that came directly from the device via \c ReceiveFromDevice(). \n If no handler could be found in this interface the parent's \c Receive() method is called. \param packet The packet. \param protocolNumber The packet's protocol number. \return - \c B_OK: Receiving was successful. - \c B_ERROR: Some error occured. - \c PPP_REJECTED: No protocol handler could be found for this packet. - \c PPP_DISCARDED: The protocol handler(s) did not handle this packet. */ status_t KPPPInterface::Receive(net_buffer *packet, uint16 protocolNumber) { TRACE("KPPPInterface: Receive(0x%X)\n", protocolNumber); if (!packet) return B_ERROR; MutexLocker locker(fLock); int32 result = PPP_REJECTED; // assume we have no handler if (protocolNumber == 0x0021 && Device() && Device()->IsUp()) { // IP_PROTOCOL 0x0021 TRACE("%s::%s: receiving IP packet\n", __FILE__, __func__); ppp_device* dev=(ppp_device*)Ifnet(); if (dev) return gStackModule->fifo_enqueue_buffer(&(dev->ppp_fifo), packet); else { dprintf("%s::%s: no ppp_device\n", __FILE__, __func__); return B_ERROR; } } // // 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 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. // If authenticating we only allow authentication phase protocols. KPPPProtocol *protocol = ProtocolFor(protocolNumber); for (; protocol; protocol = protocol->NextProtocol() ? ProtocolFor(protocolNumber, protocol->NextProtocol()) : NULL) { TRACE("KPPPInterface::Receive(): trying protocol\n"); if (!protocol->IsEnabled() || !IsProtocolAllowed(*protocol)) continue; // skip handler if disabled or not allowed result = protocol->Receive(packet, protocolNumber); if (result == PPP_UNHANDLED) continue; return result; } TRACE("KPPPInterface::Receive(): trying parent\n"); // maybe the parent interface can handle the packet if (Parent()) return Parent()->Receive(packet, protocolNumber); if (result == PPP_UNHANDLED) { gBufferModule->free(packet); return PPP_DISCARDED; } else { StateMachine().RUCEvent(packet, protocolNumber); return PPP_REJECTED; } } /*! \brief Receives a base PPP packet from the device. KPPPDevice should call this method when it receives a packet. \n PFC decoding is handled here. \param packet The packet. \return - \c B_OK: Receiving was successful. - \c B_ERROR: Some error occured. - \c PPP_REJECTED: No protocol handler could be found for this packet. - \c PPP_DISCARDED: The protocol handler(s) did not handle this packet. */ status_t KPPPInterface::ReceiveFromDevice(net_buffer *packet) { TRACE("KPPPInterface: ReceiveFromDevice()\n"); if (!packet) return B_ERROR; if (InitCheck() != B_OK) { gBufferModule->free(packet); return B_ERROR; } uint32 length = packet->size; // decode ppp frame and recognize PFC NetBufferHeaderReader bufferHeader(packet); if (bufferHeader.Status() < B_OK) return bufferHeader.Status(); uint16 &header = bufferHeader.Data(); uint16 protocolNumber = ntohs(header); // copy the protocol number TRACE("%s::%s: ppp protocol number:%x\n", __FILE__, __func__, protocolNumber); bufferHeader.Remove(); bufferHeader.Sync(); // PFC only use 1 byte for protocol, the next byte is in first byte of message if (protocolNumber & (1UL<<8)) { NetBufferPrepend bufferprepend(packet); if (bufferprepend.Status() < B_OK) { gBufferModule->free(packet); return bufferprepend.Status(); } uint8 &prepend = bufferprepend.Data(); prepend = (uint8)(protocolNumber & 0x00FF); // bufferprepend.Sync(); TRACE("%s::%s:PFC protocol:%x\n", __FILE__, __func__, protocolNumber>>8); } else { TRACE("%s::%s:Non-PFC protocol:%x\n", __FILE__, __func__, protocolNumber); } atomic_add64(&fStatistics.bytesReceived, length); atomic_add64(&fStatistics.packetsReceived, 1); return Receive(packet, protocolNumber); } //! Manages Pulse() calls for all add-ons and hanldes idle-disconnection. void KPPPInterface::Pulse() { MutexLocker locker(fLock); if (Device()) Device()->Pulse(); KPPPProtocol *protocol = FirstProtocol(); for (; protocol; protocol = protocol->NextProtocol()) protocol->Pulse(); uint32 currentTime = real_time_clock(); if (fUpdateIdleSince) { fIdleSince = currentTime; fUpdateIdleSince = false; } // check our idle time and disconnect if needed if (fDisconnectAfterIdleSince > 0 && fIdleSince != 0 && fIdleSince - currentTime >= fDisconnectAfterIdleSince) StateMachine().CloseEvent(); } //! Registers an ifnet structure for this interface. bool KPPPInterface::RegisterInterface() { TRACE("KPPPInterface: RegisterInterface()\n"); if (fIfnet) return true; // we are already registered MutexLocker locker(fLock); // only MainInterfaces get an ifnet if (IsMultilink() && Parent() && Parent()->RegisterInterface()) return true; if (!fManager) return false; fIfnet = fManager->RegisterInterface(ID()); if (!fIfnet) { dprintf("%s:%s: damn it no fIfnet device\n", __FILE__, __func__); return false; } if (DoesConnectOnDemand()) fIfnet->flags |= IFF_UP; CalculateInterfaceMTU(); CalculateBaudRate(); return true; } //! Unregisters this interface's ifnet structure. bool KPPPInterface::UnregisterInterface() { TRACE("KPPPInterface: UnregisterInterface()\n"); if (!fIfnet) return true; // we are already unregistered MutexLocker locker(fLock); // only MainInterfaces get an ifnet if (IsMultilink() && Parent()) return true; if (!fManager) return false; fManager->UnregisterInterface(ID()); // this will delete fIfnet, so do not access it anymore! fIfnet = NULL; return true; } //! Called by KPPPManager: manager routes stack ioctls to the corresponding interface. status_t KPPPInterface::StackControl(uint32 op, void *data) { TRACE("KPPPInterface: StackControl(0x%lX)\n", op); switch (op) { default: return StackControlEachHandler(op, data); } return B_OK; } //! Utility class 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; }; /*! \brief This calls Control() with the given parameters for each add-on. \return - \c B_OK: All handlers returned B_OK. - \c B_BAD_VALUE: No handler was found. - Any other value: Error code which was returned by the last failing handler. */ status_t KPPPInterface::StackControlEachHandler(uint32 op, void *data) { TRACE("KPPPInterface: StackControlEachHandler(0x%lX)\n", op); status_t result = B_BAD_VALUE, tmp; MutexLocker locker(fLock); KPPPProtocol *protocol = FirstProtocol(); for (; protocol; protocol = protocol->NextProtocol()) { tmp = protocol->StackControl(op, data); if (tmp == B_OK && result == B_BAD_VALUE) result = B_OK; else if (tmp != B_BAD_VALUE) result = tmp; } ForEachItem(LCP().fLCPExtensions, CallStackControl(op, data, result)); ForEachItem(LCP().fOptionHandlers, CallStackControl(op, data, result)); return result; } //! Recalculates the MTU from the MRU (includes encapsulation protocol overheads). void KPPPInterface::CalculateInterfaceMTU() { TRACE("KPPPInterface: CalculateInterfaceMTU()\n"); // MutexLocker locker(fLock); // uncomment this line will cause double lock // alreay locked in ::KPPPInterface fInterfaceMTU = fMRU; fHeaderLength = 2; // sum all headers (the protocol field is not counted) KPPPProtocol *protocol = FirstProtocol(); for (; protocol; protocol = protocol->NextProtocol()) { if (protocol->Level() < PPP_PROTOCOL_LEVEL) fHeaderLength += protocol->Overhead(); } fInterfaceMTU -= fHeaderLength; if (Ifnet()) { Ifnet()->mtu = fInterfaceMTU; Ifnet()->header_length = fHeaderLength; return; } if (Parent()) Parent()->CalculateInterfaceMTU(); } //! Recalculates the baud rate. void KPPPInterface::CalculateBaudRate() { TRACE("KPPPInterface: CalculateBaudRate()\n"); // MutexLocker locker(fLock); // uncomment this will cause double lock if (!Ifnet()) return; if (Device()) fIfnet->link_speed = max_c(Device()->InputTransferRate(), Device()->OutputTransferRate()); else { fIfnet->link_speed = 0; for (int32 index = 0; index < CountChildren(); index++) { if (ChildAt(index)->Ifnet()) { fIfnet->link_speed = ChildAt(index)->Ifnet()->link_speed; return; } } } } //! Reconnects. Waits a given delay (in miliseconds) before reconnecting. void KPPPInterface::Reconnect(uint32 delay) { TRACE("KPPPInterface: Reconnect(%ld)\n", delay); MutexLocker locker(fLock); if (fReconnectThread != -1) return; ++fConnectAttempt; // start a new thread that calls our Up() method reconnect_info info; info.interface = this; info.thread = &fReconnectThread; info.delay = delay; fReconnectThread = spawn_kernel_thread(reconnect_thread, "KPPPInterface: reconnect_thread", B_NORMAL_PRIORITY, NULL); resume_thread(fReconnectThread); send_data(fReconnectThread, 0, &info, sizeof(reconnect_info)); } status_t reconnect_thread(void *data) { reconnect_info info; thread_id sender; int32 code; receive_data(&sender, &info, sizeof(reconnect_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; return B_OK; } info.interface->Up(); *info.thread = -1; return B_OK; }