#include <cstdio>
#include <cstring>
#include <arpa/inet.h>
#include <ByteOrder.h>
#include <net_buffer.h>
#include <net_stack.h>
#include <ppp_device.h>
#include <KPPPInterface.h>
#include <PPPControl.h>
#include <KPPPDevice.h>
#include <KPPPLCPExtension.h>
#include <KPPPOptionHandler.h>
#include <KPPPModule.h>
#include <KPPPManager.h>
#include <KPPPUtils.h>
#include "settings_tools.h"
#include "_KPPPMRUHandler.h"
#include "_KPPPAuthenticationHandler.h"
#include "_KPPPPFCHandler.h"
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);
status_t interface_deleter_thread(void *data);
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) {
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);
if (!fSettings) {
ERROR("KPPPInterface: No fSettings!\n");
fInitStatus = B_ERROR;
return;
}
if (!AddProtocol(&LCP())) {
ERROR("KPPPInterface: Could not add LCP protocol!\n");
fInitStatus = B_ERROR;
return;
}
_KPPPMRUHandler *mruHandler =
new _KPPPMRUHandler(*this);
if (!LCP().AddOptionHandler(mruHandler) || mruHandler->InitCheck() != B_OK) {
ERROR("KPPPInterface: Could not add MRU handler!\n");
delete mruHandler;
}
_KPPPAuthenticationHandler *authenticationHandler =
new _KPPPAuthenticationHandler(*this);
if (!LCP().AddOptionHandler(authenticationHandler)
|| authenticationHandler->InitCheck() != B_OK) {
ERROR("KPPPInterface: Could not add authentication handler!\n");
delete authenticationHandler;
}
_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;
}
fConnectRetryDelay = 3000;
fReconnectDelay = 1000;
if (get_module(PPP_INTERFACE_MODULE_NAME, (module_info**) &fManager) != B_OK)
ERROR("KPPPInterface: Manager module not found!\n");
if (parent && parent->IsMultilink()) {
fParent = parent;
fParent->AddChild(this);
fIsMultilink = true;
}
RegisterInterface();
if (!fSettings) {
fInitStatus = B_ERROR;
return;
}
const char *value;
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("");
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;
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;
SetAutoReconnect(
get_boolean_value(
get_settings_value(PPP_AUTO_RECONNECT_KEY, fSettings),
false)
);
fAskBeforeConnecting = get_boolean_value(
get_settings_value(PPP_ASK_BEFORE_CONNECTING_KEY, fSettings), false);
if (!LoadModules(fSettings, 0, fSettings->parameter_count)) {
ERROR("KPPPInterface: Error loading modules!\n");
fInitStatus = B_ERROR;
}
}
KPPPInterface::~KPPPInterface()
{
TRACE("KPPPInterface: Destructor\n");
KPPPProtocol *protocol = FirstProtocol();
for (; protocol; protocol = protocol->NextProtocol())
protocol->Uninit();
UnregisterInterface();
if (fManager)
fManager->RemoveInterface(ID());
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));
int32 tmp;
send_data_with_timeout(fReconnectThread, 0, NULL, 0, 200);
wait_for_thread(fReconnectThread, &tmp);
while (CountChildren())
delete ChildAt(0);
delete Device();
while (FirstProtocol()) {
if (FirstProtocol() == &LCP())
fFirstProtocol = fFirstProtocol->NextProtocol();
else
delete FirstProtocol();
}
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);
}
void
KPPPInterface::Delete()
{
if (fDeleteCounter > 0)
return;
fDeleteCounter = 1;
fManager->DeleteInterface(ID());
}
status_t
KPPPInterface::InitCheck() const
{
if (fInitStatus != B_OK)
return fInitStatus;
if (!fSettings || !fManager)
return B_ERROR;
if (IsMultilink()) {
if (Parent() && !fDevice)
return B_ERROR;
} else if (!fDevice)
return B_ERROR;
return B_OK;
}
const char*
KPPPInterface::Username() const
{
if (Phase() < PPP_AUTHENTICATION_PHASE)
return NULL;
return fUsername;
}
const char*
KPPPInterface::Password() const
{
if (Phase() < PPP_AUTHENTICATION_PHASE)
return NULL;
return fPassword;
}
bool
KPPPInterface::SetMRU(uint32 MRU)
{
TRACE("KPPPInterface: SetMRU(%ld)\n", MRU);
if (Device() && MRU > Device()->MTU() - 2)
return false;
fMRU = MRU;
CalculateInterfaceMTU();
return true;
}
uint32
KPPPInterface::PacketOverhead() const
{
uint32 overhead = fHeaderLength + 2;
if (Device())
overhead += Device()->Overhead();
return overhead;
}
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:
{
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);
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);
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;
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);
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);
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;
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;
}
bool
KPPPInterface::SetDevice(KPPPDevice *device)
{
TRACE("KPPPInterface: SetDevice(%p)\n", device);
if (device && &device->Interface() != this)
return false;
if (IsMultilink() && !Parent())
return false;
MutexLocker locker(fLock);
if (Phase() != PPP_DOWN_PHASE)
return false;
if (fDevice && (IsUp() || fDevice->IsUp()))
Down();
fDevice = device;
SetNext(device);
if (fDevice)
fMRU = fDevice->MTU() - 2;
CalculateInterfaceMTU();
CalculateBaudRate();
return true;
}
bool
KPPPInterface::AddProtocol(KPPPProtocol *protocol)
{
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;
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);
protocol->SetNextProtocol(NULL);
protocol->SetNext(this);
} 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;
}
bool
KPPPInterface::RemoveProtocol(KPPPProtocol *protocol)
{
TRACE("KPPPInterface: RemoveProtocol(%X)\n",
protocol ? protocol->ProtocolNumber() : 0);
MutexLocker locker(fLock);
if (Phase() != PPP_DOWN_PHASE)
return false;
KPPPProtocol *current = fFirstProtocol, *previous = NULL;
while (current) {
if (current == protocol) {
if (!protocol->IsDown())
protocol->Down();
if (previous) {
previous->SetNextProtocol(current->NextProtocol());
if (!previous->Next())
previous->SetNext(this);
} else
fFirstProtocol = current->NextProtocol();
current->SetNextProtocol(NULL);
CalculateInterfaceMTU();
return true;
}
previous = current;
current = current->NextProtocol();
}
return false;
}
int32
KPPPInterface::CountProtocols() const
{
MutexLocker locker(fLock);
KPPPProtocol *protocol = FirstProtocol();
int32 count = 0;
for (; protocol; protocol = protocol->NextProtocol())
++count;
return count;
}
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;
}
KPPPProtocol*
KPPPInterface::ProtocolFor(uint16 protocolNumber, KPPPProtocol *start) const
{
TRACE("KPPPInterface: ProtocolFor(%X)\n", protocolNumber);
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;
}
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;
}
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);
if (CountChildren() == 0 && fManager && Ifnet())
Delete();
return true;
}
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;
}
void
KPPPInterface::SetAutoReconnect(bool autoReconnect)
{
TRACE("KPPPInterface: SetAutoReconnect(%s)\n", autoReconnect ? "true" : "false");
if (Mode() != PPP_CLIENT_MODE)
return;
fAutoReconnect = autoReconnect;
}
void
KPPPInterface::SetConnectOnDemand(bool connectOnDemand)
{
TRACE("KPPPInterface: SetConnectOnDemand(%s)\n", connectOnDemand ? "true" : "false");
MutexLocker locker(fLock);
if (Mode() != PPP_CLIENT_MODE) {
TRACE("KPPPInterface::SetConnectOnDemand(): Wrong mode!\n");
fConnectOnDemand = false;
return;
} else if (DoesConnectOnDemand() == connectOnDemand)
return;
fConnectOnDemand = connectOnDemand;
if (!Parent() && State() == PPP_INITIAL_STATE && Phase() == PPP_DOWN_PHASE) {
if (!connectOnDemand)
Delete();
return;
}
if (connectOnDemand) {
if (Ifnet())
Ifnet()->flags |= IFF_UP;
} else if (!connectOnDemand && Phase() < PPP_ESTABLISHED_PHASE) {
if (Ifnet())
Ifnet()->flags &= ~IFF_UP;
}
}
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) {
StateMachine().ContinueOpenEvent();
}
}
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;
}
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;
}
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);
MutexLocker locker(fLock);
StateMachine().CloseEvent();
return true;
}
bool
KPPPInterface::WaitForConnection()
{
TRACE("KPPPInterface: WaitForConnection()\n");
if (InitCheck() != B_OK)
return false;
for (uint32 i = 0; i < 10000; i++)
for (uint32 j = 0; j < 3000000; j++)
return true;
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;
}
bool
KPPPInterface::LoadModules(driver_settings *settings, int32 start, int32 count)
{
TRACE("KPPPInterface: LoadModules()\n");
if (Phase() != PPP_DOWN_PHASE)
return false;
ppp_module_key_type type;
const char *name = NULL;
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;
}
}
if (IsMultilink() && !Parent()) {
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;
}
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;
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;
}
fModules.AddItem(moduleName);
return module->add_to(Parent() ? *Parent() : *this, this, parameter, type);
}
bool
KPPPInterface::IsAllowedToSend() const
{
return true;
}
status_t
KPPPInterface::Send(net_buffer *packet, uint16 protocolNumber)
{
TRACE("KPPPInterface: Send(0x%X)\n", protocolNumber);
if (!packet)
return B_ERROR;
if (InitCheck() != B_OK) {
ERROR("InitCheck() fail\n");
gBufferModule->free(packet);
return B_ERROR;
}
if ((protocolNumber != PPP_LCP_PROTOCOL && DoesConnectOnDemand()
&& (Phase() == PPP_DOWN_PHASE
|| Phase() == PPP_ESTABLISHMENT_PHASE)
&& !Up()) && !WaitForConnection()) {
dprintf("DoesConnectOnDemand fail!\n");
return B_ERROR;
}
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
if (Device()->IsUp() && protocolNumber == 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");
return B_ERROR;
}
if (UseLocalPFC() && (protocolNumber & 0xFF00) == 0) {
TRACE("%s::%s should not go here\n", __FILE__, __func__);
NetBufferPrepend<uint8> bufferHeader(packet);
if (bufferHeader.Status() != B_OK)
return bufferHeader.Status();
uint8 &header = bufferHeader.Data();
header = (protocolNumber & 0x00FF);
} else {
NetBufferPrepend<uint16> bufferHeader(packet);
if (bufferHeader.Status() != B_OK)
return bufferHeader.Status();
uint16 &header = bufferHeader.Data();
header = htons(protocolNumber);
}
if (!IsMultilink() || Parent()) {
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);
} else {
TRACE("It is weird to go here!\n");
gBufferModule->free(packet);
return B_ERROR;
}
}
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;
if (protocolNumber == 0x0021 && Device() && Device()->IsUp()) {
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;
}
}
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;
result = protocol->Receive(packet, protocolNumber);
if (result == PPP_UNHANDLED)
continue;
return result;
}
TRACE("KPPPInterface::Receive(): trying parent\n");
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;
}
}
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;
NetBufferHeaderReader<uint16> bufferHeader(packet);
if (bufferHeader.Status() < B_OK)
return bufferHeader.Status();
uint16 &header = bufferHeader.Data();
uint16 protocolNumber = ntohs(header);
TRACE("%s::%s: ppp protocol number:%x\n", __FILE__, __func__, protocolNumber);
bufferHeader.Remove();
bufferHeader.Sync();
if (protocolNumber & (1UL<<8)) {
NetBufferPrepend<uint8> bufferprepend(packet);
if (bufferprepend.Status() < B_OK) {
gBufferModule->free(packet);
return bufferprepend.Status();
}
uint8 &prepend = bufferprepend.Data();
prepend = (uint8)(protocolNumber & 0x00FF);
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);
}
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;
}
if (fDisconnectAfterIdleSince > 0 && fIdleSince != 0
&& fIdleSince - currentTime >= fDisconnectAfterIdleSince)
StateMachine().CloseEvent();
}
bool
KPPPInterface::RegisterInterface()
{
TRACE("KPPPInterface: RegisterInterface()\n");
if (fIfnet)
return true;
MutexLocker locker(fLock);
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;
}
bool
KPPPInterface::UnregisterInterface()
{
TRACE("KPPPInterface: UnregisterInterface()\n");
if (!fIfnet)
return true;
MutexLocker locker(fLock);
if (IsMultilink() && Parent())
return true;
if (!fManager)
return false;
fManager->UnregisterInterface(ID());
fIfnet = NULL;
return true;
}
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;
}
template<class T>
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;
};
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<KPPPLCPExtension>(op, data, result));
ForEachItem(LCP().fOptionHandlers,
CallStackControl<KPPPOptionHandler>(op, data, result));
return result;
}
void
KPPPInterface::CalculateInterfaceMTU()
{
TRACE("KPPPInterface: CalculateInterfaceMTU()\n");
fInterfaceMTU = fMRU;
fHeaderLength = 2;
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();
}
void
KPPPInterface::CalculateBaudRate()
{
TRACE("KPPPInterface: CalculateBaudRate()\n");
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;
}
}
}
}
void
KPPPInterface::Reconnect(uint32 delay)
{
TRACE("KPPPInterface: Reconnect(%ld)\n", delay);
MutexLocker locker(fLock);
if (fReconnectThread != -1)
return;
++fConnectAttempt;
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));
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;
}