Finished PPP interface manager module.
TODO: - IPCP protocol module - PAP authentication module - PPPoE device module - pppdial cmd-line app including needed net_stack_driver changes for control() function support - tests, tests, tests :)) git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5040 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -6,50 +6,172 @@
|
||||
//---------------------------------------------------------------------
|
||||
|
||||
#include "PPPManager.h"
|
||||
#include <PPPControl.h>
|
||||
#include <KPPPUtils.h>
|
||||
|
||||
#include <core_funcs.h>
|
||||
#include <kernel_cpp.h>
|
||||
#include <LockerHelper.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/sockio.h>
|
||||
|
||||
|
||||
static int ppp_ifnet_stop(ifnet *ifp)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int ppp_ifnet_output(ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
|
||||
struct rtentry *rt0)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int ppp_ifnet_ioctl(struct ifnet *ifp, ulong cmd, caddr_t data)
|
||||
{
|
||||
return B_ERROR;
|
||||
}
|
||||
#ifdef _KERNEL_MODE
|
||||
#define spawn_thread spawn_kernel_thread
|
||||
#endif
|
||||
|
||||
|
||||
static const char ppp_if_name_base[] = "ppp";
|
||||
|
||||
|
||||
static
|
||||
status_t
|
||||
deleter_thread(void *data)
|
||||
{
|
||||
PPPManager *manager = (PPPManager*) data;
|
||||
thread_id sender;
|
||||
int32 code;
|
||||
|
||||
while(true) {
|
||||
manager->DeleterThreadEvent();
|
||||
|
||||
// check if the manager is being destroyed
|
||||
if(receive_data_with_timeout(&sender, &code, NULL, 0, 50000) == B_OK)
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
void
|
||||
pulse_timer(void *data)
|
||||
{
|
||||
((PPPManager*)data)->Pulse();
|
||||
}
|
||||
|
||||
|
||||
PPPManager::PPPManager()
|
||||
: fReportManager(fLock),
|
||||
: fReportManager(fReportLock),
|
||||
fNextID(1)
|
||||
{
|
||||
fDeleterThread = spawn_thread(deleter_thread, "PPPManager: deleter_thread",
|
||||
B_NORMAL_PRIORITY, this);
|
||||
resume_thread(fDeleterThread);
|
||||
fPulseTimer = net_add_timer(pulse_timer, this, PPP_PULSE_RATE);
|
||||
}
|
||||
|
||||
|
||||
PPPManager::~PPPManager()
|
||||
{
|
||||
int32 tmp;
|
||||
send_data(fDeleterThread, 0, NULL, 0);
|
||||
// notify deleter_thread that we are being destroyed
|
||||
wait_for_thread(fDeleterThread, &tmp);
|
||||
net_remove_timer(fPulseTimer);
|
||||
|
||||
// now really delete the interfaces (the deleter_thread is not running)
|
||||
interface_entry *entry;
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(entry)
|
||||
delete entry->interface;
|
||||
delete entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
PPPManager::Stop(ifnet *ifp)
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
interface_entry *entry = EntryFor(ifp);
|
||||
if(!entry)
|
||||
return B_ERROR;
|
||||
|
||||
DeleteInterface(entry->interface->ID());
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
PPPManager::Output(ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
|
||||
struct rtentry *rt0)
|
||||
{
|
||||
if(dst->sa_family == AF_UNSPEC)
|
||||
return B_ERROR;
|
||||
|
||||
LockerHelper locker(fLock);
|
||||
interface_entry *entry = EntryFor(ifp);
|
||||
if(!entry)
|
||||
return B_ERROR;
|
||||
|
||||
++entry->accessing;
|
||||
locker.UnlockNow();
|
||||
|
||||
if(ifp->if_flags & (IFF_UP | IFF_RUNNING) != (IFF_UP | IFF_RUNNING)) {
|
||||
m_freem(buf);
|
||||
--entry->accessing;
|
||||
return ENETDOWN;
|
||||
}
|
||||
|
||||
int32 result = B_ERROR;
|
||||
PPPProtocol *protocol = entry->interface->FirstProtocol();
|
||||
for(; protocol; protocol = protocol->NextProtocol()) {
|
||||
if(!protocol->IsEnabled() || protocol->AddressFamily() != dst->sa_family)
|
||||
continue;
|
||||
|
||||
result = protocol->Send(buf, protocol->ProtocolNumber());
|
||||
if(result == PPP_UNHANDLED)
|
||||
continue;
|
||||
|
||||
--entry->accessing;
|
||||
return result;
|
||||
}
|
||||
|
||||
m_freem(buf);
|
||||
--entry->accessing;
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
PPPManager::Control(ifnet *ifp, ulong cmd, caddr_t data)
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
interface_entry *entry = EntryFor(ifp);
|
||||
if(!entry || entry->deleting)
|
||||
return B_ERROR;
|
||||
|
||||
++entry->accessing;
|
||||
locker.UnlockNow();
|
||||
|
||||
switch(cmd) {
|
||||
case SIOCSIFFLAGS:
|
||||
if(((ifreq*)data)->ifr_flags & IFF_DOWN)
|
||||
DeleteInterface(entry->interface->ID());
|
||||
else if(((ifreq*)data)->ifr_flags & IFF_UP) {
|
||||
int32 result = entry->interface->Up() ? B_OK : B_ERROR;
|
||||
--entry->accessing;
|
||||
return result;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return entry->interface->StackControl(cmd, data);
|
||||
}
|
||||
|
||||
--entry->accessing;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
interface_id
|
||||
PPPManager::CreateInterface(const driver_settings *settings, interface_id parentID)
|
||||
PPPManager::CreateInterface(const driver_settings *settings,
|
||||
interface_id parentID = PPP_UNDEFINED_INTERFACE_ID)
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
@@ -73,6 +195,12 @@ PPPManager::CreateInterface(const driver_settings *settings, interface_id parent
|
||||
fEntries.AddItem(entry);
|
||||
locker.UnlockNow();
|
||||
|
||||
if(Report(PPP_MANAGER_REPORT, PPP_REPORT_INTERFACE_CREATED,
|
||||
&id, sizeof(interface_id)) != B_OK) {
|
||||
DeleteInterface(id);
|
||||
return PPP_UNDEFINED_INTERFACE_ID;
|
||||
}
|
||||
|
||||
if(!entry->interface->DoesDialOnDemand())
|
||||
entry->interface->Up();
|
||||
|
||||
@@ -98,9 +226,9 @@ PPPManager::RemoveInterface(interface_id ID)
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
int32 index = 0;
|
||||
int32 index;
|
||||
interface_entry *entry = EntryFor(ID, &index);
|
||||
if(!entry)
|
||||
if(!entry || entry->deleting)
|
||||
return;
|
||||
|
||||
UnregisterInterface(ID);
|
||||
@@ -116,7 +244,7 @@ PPPManager::RegisterInterface(interface_id ID)
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
interface_entry *entry = EntryFor(ID);
|
||||
if(!entry)
|
||||
if(!entry || entry->deleting)
|
||||
return NULL;
|
||||
|
||||
// maybe the interface is already registered
|
||||
@@ -144,21 +272,117 @@ PPPManager::RegisterInterface(interface_id ID)
|
||||
bool
|
||||
PPPManager::UnregisterInterface(interface_id ID)
|
||||
{
|
||||
return false;
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
interface_entry *entry = EntryFor(ID);
|
||||
if(!entry)
|
||||
return false;
|
||||
|
||||
if(entry->interface->Ifnet()) {
|
||||
if_detach(entry->interface->Ifnet());
|
||||
delete entry->interface->Ifnet();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PPPManager::Control(uint32 op, void *data, size_t length)
|
||||
{
|
||||
return B_ERROR;
|
||||
// this method is intended for use by userland applications
|
||||
|
||||
switch(op) {
|
||||
case PPPC_CREATE_INTERFACE: {
|
||||
if(length < sizeof(pppc_create_interface_info) || !data)
|
||||
return B_ERROR;
|
||||
|
||||
pppc_create_interface_info *info = (pppc_create_interface_info*) data;
|
||||
if(!info->settings)
|
||||
return B_ERROR;
|
||||
|
||||
info->resultID = CreateInterface(info->settings);
|
||||
// parents cannot be set from userland
|
||||
return info->resultID != PPP_UNDEFINED_INTERFACE_ID ? B_OK : B_ERROR;
|
||||
} break;
|
||||
|
||||
case PPPC_DELETE_INTERFACE:
|
||||
if(length != sizeof(interface_id) || !data)
|
||||
return B_ERROR;
|
||||
|
||||
DeleteInterface(*(interface_id*)data);
|
||||
break;
|
||||
|
||||
case PPPC_CONTROL_INTERFACE: {
|
||||
if(length < sizeof(ppp_control_info) || !data)
|
||||
return B_ERROR;
|
||||
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
ppp_control_info *control = (ppp_control_info*) data;
|
||||
interface_entry *entry = EntryFor(control->index);
|
||||
if(!entry)
|
||||
return B_BAD_INDEX;
|
||||
|
||||
return entry->interface->Control(control->op, control->data,
|
||||
control->length);
|
||||
} break;
|
||||
|
||||
case PPPC_GET_INTERFACES: {
|
||||
if(length < sizeof(pppc_get_interfaces_info) || !data)
|
||||
return B_ERROR;
|
||||
|
||||
pppc_get_interfaces_info *info = (pppc_get_interfaces_info*) data;
|
||||
if(!info->interfaces)
|
||||
return B_ERROR;
|
||||
|
||||
info->resultCount = GetInterfaces(info->interfaces, info->count,
|
||||
info->filter);
|
||||
return info->resultCount >= 0 ? B_OK : B_ERROR;
|
||||
} break;
|
||||
|
||||
case PPPC_COUNT_INTERFACES:
|
||||
if(length != sizeof(ppp_interface_filter) || !data)
|
||||
return B_ERROR;
|
||||
|
||||
return CountInterfaces(*(ppp_interface_filter*)data);
|
||||
break;
|
||||
|
||||
case PPPC_ENABLE_MANAGER_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_MANAGER_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;
|
||||
|
||||
default:
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
PPPManager::ControlInterface(interface_id ID, uint32 op, void *data, size_t length)
|
||||
{
|
||||
return B_ERROR;
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
interface_entry *entry = EntryFor(ID);
|
||||
if(entry && !entry->deleting)
|
||||
return entry->interface->Control(op, data, length);
|
||||
|
||||
return B_BAD_INDEX;
|
||||
}
|
||||
|
||||
|
||||
@@ -166,30 +390,111 @@ int32
|
||||
PPPManager::GetInterfaces(interface_id *interfaces, int32 count,
|
||||
ppp_interface_filter filter = PPP_REGISTERED_INTERFACES)
|
||||
{
|
||||
return 0;
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
int32 item = 0;
|
||||
// the current item in 'interfaces' (also the number of ids copied)
|
||||
interface_entry *entry;
|
||||
|
||||
for(int32 index = 0; item < count && index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(!entry || entry->deleting)
|
||||
continue;
|
||||
|
||||
switch(filter) {
|
||||
case PPP_ALL_INTERFACES:
|
||||
interfaces[item++] = entry->interface->ID();
|
||||
break;
|
||||
|
||||
case PPP_REGISTERED_INTERFACES:
|
||||
if(entry->interface->Ifnet())
|
||||
interfaces[item++] = entry->interface->ID();
|
||||
break;
|
||||
|
||||
case PPP_UNREGISTERED_INTERFACES:
|
||||
if(!entry->interface->Ifnet())
|
||||
interfaces[item++] = entry->interface->ID();
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
// the filter is unknown
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
PPPManager::CountInterfaces(ppp_interface_filter filter = PPP_REGISTERED_INTERFACES)
|
||||
{
|
||||
return 0;
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
if(filter == PPP_ALL_INTERFACES)
|
||||
return fEntries.CountItems();
|
||||
|
||||
int32 count = 0;
|
||||
interface_entry *entry;
|
||||
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(!entry || entry->deleting)
|
||||
continue;
|
||||
|
||||
switch(filter) {
|
||||
case PPP_REGISTERED_INTERFACES:
|
||||
if(entry->interface->Ifnet())
|
||||
++count;
|
||||
break;
|
||||
|
||||
case PPP_UNREGISTERED_INTERFACES:
|
||||
if(!entry->interface->Ifnet())
|
||||
++count;
|
||||
break;
|
||||
|
||||
default:
|
||||
return B_ERROR;
|
||||
// the filter is unknown
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
interface_entry*
|
||||
PPPManager::EntryFor(interface_id ID, int32 *start = NULL) const
|
||||
PPPManager::EntryFor(interface_id ID, int32 *saveIndex = NULL) const
|
||||
{
|
||||
if(ID == PPP_UNDEFINED_INTERFACE_ID)
|
||||
return NULL;
|
||||
|
||||
interface_entry *entry;
|
||||
|
||||
for(int32 index = start ? *start : 0; index < fEntries.CountItems(); index++) {
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(entry && entry->interface->ID() == ID) {
|
||||
if(start)
|
||||
*start = index;
|
||||
if(saveIndex)
|
||||
*saveIndex = index;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
interface_entry*
|
||||
PPPManager::EntryFor(ifnet *ifp, int32 *saveIndex = NULL) const
|
||||
{
|
||||
if(!ifp)
|
||||
return NULL;
|
||||
|
||||
interface_entry *entry;
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(entry && entry->interface->Ifnet() == ifp) {
|
||||
if(saveIndex)
|
||||
*saveIndex = index;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
@@ -233,3 +538,42 @@ PPPManager::FindUnit() const
|
||||
|
||||
return unit;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PPPManager::DeleterThreadEvent()
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
// delete and remove marked interfaces
|
||||
interface_entry *entry;
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(!entry) {
|
||||
fEntries.RemoveItem(index);
|
||||
--index;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(entry->deleting && entry->accessing == 0) {
|
||||
delete entry->interface;
|
||||
delete entry;
|
||||
fEntries.RemoveItem(index);
|
||||
--index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
PPPManager::Pulse()
|
||||
{
|
||||
LockerHelper locker(fLock);
|
||||
|
||||
interface_entry *entry;
|
||||
for(int32 index = 0; index < fEntries.CountItems(); index++) {
|
||||
entry = fEntries.ItemAt(index);
|
||||
if(entry)
|
||||
entry->interface->Pulse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,14 @@
|
||||
|
||||
#include <KPPPInterface.h>
|
||||
#include <KPPPManager.h>
|
||||
#include <core_funcs.h>
|
||||
|
||||
|
||||
// these functions are defined in ppp.cpp
|
||||
extern int ppp_ifnet_stop(ifnet *ifp);
|
||||
extern int ppp_ifnet_output(ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
|
||||
struct rtentry *rt0);
|
||||
extern int ppp_ifnet_ioctl(ifnet *ifp, ulong cmd, caddr_t data);
|
||||
|
||||
|
||||
typedef struct interface_entry {
|
||||
@@ -24,8 +32,13 @@ class PPPManager {
|
||||
PPPManager();
|
||||
~PPPManager();
|
||||
|
||||
int32 Stop(ifnet *ifp);
|
||||
int32 Output(ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
|
||||
struct rtentry *rt0);
|
||||
int32 Control(ifnet *ifp, ulong cmd, caddr_t data);
|
||||
|
||||
interface_id CreateInterface(const driver_settings *settings,
|
||||
interface_id parentID);
|
||||
interface_id parentID = PPP_UNDEFINED_INTERFACE_ID);
|
||||
void DeleteInterface(interface_id ID);
|
||||
void RemoveInterface(interface_id ID);
|
||||
|
||||
@@ -46,19 +59,25 @@ class PPPManager {
|
||||
{ return ReportManager().Report(type, code, data, length); }
|
||||
// returns false if reply was bad (or an error occured)
|
||||
|
||||
interface_entry *EntryFor(interface_id ID, int32 *start = NULL) const;
|
||||
interface_entry *EntryFor(interface_id ID, int32 *saveIndex = NULL) const;
|
||||
interface_entry *EntryFor(ifnet *ifp, int32 *saveIndex = NULL) const;
|
||||
|
||||
interface_id NextID()
|
||||
{ return (interface_id) atomic_add((vint32*) &fNextID, 1); }
|
||||
|
||||
void DeleterThreadEvent();
|
||||
void Pulse();
|
||||
|
||||
private:
|
||||
int32 FindUnit() const;
|
||||
|
||||
private:
|
||||
BLocker fLock;
|
||||
BLocker fLock, fReportLock;
|
||||
PPPReportManager fReportManager;
|
||||
List<interface_entry*> fEntries;
|
||||
interface_id fNextID;
|
||||
thread_id fDeleterThread;
|
||||
net_timer_id fPulseTimer;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -21,6 +21,37 @@ struct core_module_info *core = NULL;
|
||||
static PPPManager *manager = NULL;
|
||||
|
||||
|
||||
int
|
||||
ppp_ifnet_stop(ifnet *ifp)
|
||||
{
|
||||
if(manager)
|
||||
return manager->Stop(ifp);
|
||||
else
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ppp_ifnet_output(ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
|
||||
struct rtentry *rt0)
|
||||
{
|
||||
if(manager)
|
||||
return manager->Output(ifp, buf, dst, rt0);
|
||||
else
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
ppp_ifnet_ioctl(ifnet *ifp, ulong cmd, caddr_t data)
|
||||
{
|
||||
if(manager)
|
||||
return manager->Control(ifp, cmd, data);
|
||||
else
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
static
|
||||
int
|
||||
ppp_start(void *cpp)
|
||||
@@ -39,6 +70,7 @@ int
|
||||
ppp_stop()
|
||||
{
|
||||
delete manager;
|
||||
manager = NULL;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user