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:
Waldemar Kornewald
2003-10-15 16:01:40 +00:00
parent 4c4cad9f02
commit 0fc3ae8c5c
3 changed files with 431 additions and 36 deletions
@@ -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;
}