* Completed the previous commit and merger of the team/network/new_stack branch.
* Removed ppp_up and pppcontrol from the image for now. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18457 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -0,0 +1,610 @@
|
||||
/*
|
||||
* Copyright 2006, Haiku, Inc. All Rights Reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*
|
||||
* Authors:
|
||||
* Axel Dörfler, [email protected]
|
||||
*/
|
||||
|
||||
|
||||
#include "domains.h"
|
||||
#include "interfaces.h"
|
||||
#include "stack_private.h"
|
||||
|
||||
#include <net_device.h>
|
||||
|
||||
#include <lock.h>
|
||||
#include <util/AutoLock.h>
|
||||
|
||||
#include <KernelExport.h>
|
||||
|
||||
#include <net/if_dl.h>
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#define TRACE_INTERFACES
|
||||
#ifdef TRACE_INTERFACES
|
||||
# define TRACE(x) dprintf x
|
||||
#else
|
||||
# define TRACE(x) ;
|
||||
#endif
|
||||
|
||||
|
||||
static benaphore sInterfaceLock;
|
||||
static list sInterfaces;
|
||||
static uint32 sInterfaceIndex;
|
||||
static uint32 sDeviceIndex;
|
||||
|
||||
|
||||
static net_device_interface *
|
||||
find_device_interface(const char *name)
|
||||
{
|
||||
net_device_interface *interface = NULL;
|
||||
|
||||
while (true) {
|
||||
interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
if (!strcmp(interface->name, name))
|
||||
return interface;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
domain_receive_adapter(void *cookie, net_buffer *buffer)
|
||||
{
|
||||
net_domain_private *domain = (net_domain_private *)cookie;
|
||||
return domain->module->receive_data(buffer);
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - interfaces
|
||||
|
||||
|
||||
/*!
|
||||
Searches for a specific interface in a domain by name.
|
||||
You need to have the domain's lock hold when calling this function.
|
||||
*/
|
||||
struct net_interface_private *
|
||||
find_interface(struct net_domain *domain, const char *name)
|
||||
{
|
||||
net_interface_private *interface = NULL;
|
||||
|
||||
while (true) {
|
||||
interface = (net_interface_private *)list_get_next_item(
|
||||
&domain->interfaces, interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
if (!strcmp(interface->name, name))
|
||||
return interface;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Searches for a specific interface in a domain by index.
|
||||
You need to have the domain's lock hold when calling this function.
|
||||
*/
|
||||
struct net_interface_private *
|
||||
find_interface(struct net_domain *domain, uint32 index)
|
||||
{
|
||||
net_interface_private *interface = NULL;
|
||||
|
||||
while (true) {
|
||||
interface = (net_interface_private *)list_get_next_item(
|
||||
&domain->interfaces, interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
if (interface->index == index)
|
||||
return interface;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
create_interface(net_domain *domain, const char *name, const char *baseName,
|
||||
net_device_interface *deviceInterface, net_interface_private **_interface)
|
||||
{
|
||||
net_interface_private *interface =
|
||||
new (std::nothrow) net_interface_private;
|
||||
if (interface == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
strlcpy(interface->name, name, IF_NAMESIZE);
|
||||
strlcpy(interface->base_name, baseName, IF_NAMESIZE);
|
||||
interface->domain = domain;
|
||||
interface->device = deviceInterface->device;
|
||||
|
||||
interface->address = NULL;
|
||||
interface->destination = NULL;
|
||||
interface->mask = NULL;
|
||||
|
||||
interface->index = ++sInterfaceIndex;
|
||||
interface->flags = deviceInterface->device->flags & ~IFF_UP;
|
||||
interface->type = 0;
|
||||
interface->mtu = deviceInterface->device->mtu;
|
||||
interface->metric = 0;
|
||||
interface->device_interface = deviceInterface;
|
||||
|
||||
status_t status = get_domain_datalink_protocols(interface);
|
||||
if (status < B_OK) {
|
||||
delete interface;
|
||||
return status;
|
||||
}
|
||||
|
||||
// Grab a reference to the networking stack, to make sure it won't be
|
||||
// unloaded as long as an interface exists
|
||||
module_info *module;
|
||||
get_module(NET_STARTER_MODULE_NAME, &module);
|
||||
|
||||
*_interface = interface;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
delete_interface(net_interface_private *interface)
|
||||
{
|
||||
put_device_interface(interface->device_interface);
|
||||
|
||||
free(interface->address);
|
||||
free(interface->destination);
|
||||
free(interface->mask);
|
||||
|
||||
delete interface;
|
||||
|
||||
// Release reference of the stack - at this point, our stack may be unloaded
|
||||
// if no other interfaces or sockets are left
|
||||
put_module(NET_STARTER_MODULE_NAME);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
put_interface(struct net_interface_private *interface)
|
||||
{
|
||||
// TODO: reference counting
|
||||
// TODO: better locking scheme
|
||||
benaphore_unlock(&((net_domain_private *)interface->domain)->lock);
|
||||
}
|
||||
|
||||
|
||||
struct net_interface_private *
|
||||
get_interface(net_domain *_domain, const char *name)
|
||||
{
|
||||
net_domain_private *domain = (net_domain_private *)_domain;
|
||||
benaphore_lock(&domain->lock);
|
||||
|
||||
net_interface_private *interface = NULL;
|
||||
while (true) {
|
||||
interface = (net_interface_private *)list_get_next_item(
|
||||
&domain->interfaces, interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
if (!strcmp(interface->name, name))
|
||||
return interface;
|
||||
}
|
||||
|
||||
benaphore_unlock(&domain->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - device interfaces
|
||||
|
||||
|
||||
void
|
||||
get_device_interface_address(net_device_interface *interface, sockaddr *_address)
|
||||
{
|
||||
sockaddr_dl &address = *(sockaddr_dl *)_address;
|
||||
|
||||
address.sdl_family = AF_LINK;
|
||||
address.sdl_index = interface->device->index;
|
||||
address.sdl_type = interface->device->type;
|
||||
address.sdl_nlen = strlen(interface->name);
|
||||
address.sdl_slen = 0;
|
||||
memcpy(address.sdl_data, interface->name, address.sdl_nlen);
|
||||
|
||||
address.sdl_alen = interface->device->address.length;
|
||||
memcpy(LLADDR(&address), interface->device->address.data, address.sdl_alen);
|
||||
|
||||
address.sdl_len = sizeof(sockaddr_dl) - sizeof(address.sdl_data)
|
||||
+ address.sdl_nlen + address.sdl_alen;
|
||||
}
|
||||
|
||||
|
||||
uint32
|
||||
count_device_interfaces()
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
net_device_interface *interface = NULL;
|
||||
uint32 count = 0;
|
||||
|
||||
while (true) {
|
||||
interface = (net_device_interface *)list_get_next_item(&sInterfaces,
|
||||
interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Dumps a list of all interfaces into the supplied userland buffer.
|
||||
If the interfaces don't fit into the buffer, an error (\c ENOBUFS) is
|
||||
returned.
|
||||
*/
|
||||
status_t
|
||||
list_device_interfaces(void *buffer, size_t size)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
net_device_interface *interface = NULL;
|
||||
size_t spaceLeft = size;
|
||||
|
||||
while (true) {
|
||||
interface = (net_device_interface *)list_get_next_item(&sInterfaces,
|
||||
interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
ifreq request;
|
||||
strlcpy(request.ifr_name, interface->name, IF_NAMESIZE);
|
||||
get_device_interface_address(interface, &request.ifr_addr);
|
||||
|
||||
size = IF_NAMESIZE + request.ifr_addr.sa_len;
|
||||
if (spaceLeft < size)
|
||||
return ENOBUFS;
|
||||
|
||||
if (user_memcpy(buffer, &request, size) < B_OK)
|
||||
return B_BAD_ADDRESS;
|
||||
|
||||
buffer = (void *)((addr_t)buffer + size);
|
||||
spaceLeft -= size;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Releases the reference for the interface. When all references are
|
||||
released, the interface is removed.
|
||||
*/
|
||||
void
|
||||
put_device_interface(struct net_device_interface *interface)
|
||||
{
|
||||
if (atomic_add(&interface->ref_count, -1) != 1)
|
||||
return;
|
||||
|
||||
// we need to remove this interface!
|
||||
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
list_remove_item(&sInterfaces, interface);
|
||||
}
|
||||
|
||||
interface->module->uninit_device(interface->device);
|
||||
put_module(interface->module->info.name);
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Finds an interface by the specified index and grabs a reference to it.
|
||||
*/
|
||||
struct net_device_interface *
|
||||
get_device_interface(uint32 index)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
net_device_interface *interface = NULL;
|
||||
|
||||
while (true) {
|
||||
interface = (net_device_interface *)list_get_next_item(&sInterfaces, interface);
|
||||
if (interface == NULL)
|
||||
break;
|
||||
|
||||
if (interface->device->index == index) {
|
||||
if (atomic_add(&interface->ref_count, 1) != 0)
|
||||
return interface;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Finds an interface by the specified name and grabs a reference to it.
|
||||
If the interface does not yet exist, a new one is created.
|
||||
*/
|
||||
struct net_device_interface *
|
||||
get_device_interface(const char *name)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
net_device_interface *interface = find_device_interface(name);
|
||||
if (interface != NULL) {
|
||||
if (atomic_add(&interface->ref_count, 1) != 0)
|
||||
return interface;
|
||||
|
||||
// try to recreate interface - it just got removed
|
||||
}
|
||||
|
||||
void *cookie = open_module_list("network/devices");
|
||||
if (cookie == NULL)
|
||||
return NULL;
|
||||
|
||||
while (true) {
|
||||
char moduleName[B_FILE_NAME_LENGTH];
|
||||
size_t length = sizeof(moduleName);
|
||||
if (read_next_module_name(cookie, moduleName, &length) != B_OK)
|
||||
break;
|
||||
|
||||
TRACE(("get_device_interface: ask \"%s\" for %s\n", moduleName, name));
|
||||
|
||||
net_device_module_info *module;
|
||||
if (get_module(moduleName, (module_info **)&module) == B_OK) {
|
||||
net_device *device;
|
||||
status_t status = module->init_device(name, &device);
|
||||
if (status == B_OK) {
|
||||
// create new module interface for this
|
||||
interface = new (std::nothrow) net_device_interface;
|
||||
if (interface != NULL) {
|
||||
interface->name = device->name;
|
||||
interface->module = module;
|
||||
interface->device = device;
|
||||
interface->up_count = 0;
|
||||
interface->ref_count = 1;
|
||||
interface->deframe_func = NULL;
|
||||
interface->deframe_ref_count = 0;
|
||||
|
||||
device->index = ++sDeviceIndex;
|
||||
device->module = module;
|
||||
|
||||
list_add_item(&sInterfaces, interface);
|
||||
return interface;
|
||||
} else
|
||||
module->uninit_device(device);
|
||||
}
|
||||
|
||||
put_module(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - devices
|
||||
|
||||
|
||||
/*!
|
||||
Unregisters a previously registered deframer function.
|
||||
This function is part of the net_manager_module_info API.
|
||||
*/
|
||||
status_t
|
||||
unregister_device_deframer(net_device *device)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
if (--interface->deframe_ref_count == 0)
|
||||
interface->deframe_func = NULL;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
Registers the deframer function for the specified \a device.
|
||||
Note, however, that right now, you can only register one single
|
||||
deframer function per device.
|
||||
|
||||
If the need arises, we might want to lift that limitation at a
|
||||
later time (which would require a slight API change, though).
|
||||
|
||||
This function is part of the net_manager_module_info API.
|
||||
*/
|
||||
status_t
|
||||
register_device_deframer(net_device *device, net_deframe_func deframeFunc)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
if (interface->deframe_func != NULL && interface->deframe_func != deframeFunc)
|
||||
return B_ERROR;
|
||||
|
||||
interface->deframe_func = deframeFunc;
|
||||
interface->deframe_ref_count++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
register_domain_device_handler(struct net_device *device, int32 type,
|
||||
struct net_domain *_domain)
|
||||
{
|
||||
net_domain_private *domain = (net_domain_private *)_domain;
|
||||
if (domain->module == NULL || domain->module->receive_data == NULL)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
return register_device_handler(device, type, &domain_receive_adapter, domain);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
register_device_handler(struct net_device *device, int32 type,
|
||||
net_receive_func receiveFunc, void *cookie)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
// see if such a handler already for this device
|
||||
|
||||
DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator();
|
||||
while (iterator.HasNext()) {
|
||||
net_device_handler *handler = iterator.Next();
|
||||
|
||||
if (handler->type == type)
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
// Add new handler
|
||||
|
||||
net_device_handler *handler = new (std::nothrow) net_device_handler;
|
||||
if (handler == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
handler->func = receiveFunc;
|
||||
handler->type = type;
|
||||
handler->cookie = cookie;
|
||||
interface->receive_funcs.Add(handler);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
unregister_device_handler(struct net_device *device, int32 type)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
// search for the handler
|
||||
|
||||
DeviceHandlerList::Iterator iterator = interface->receive_funcs.GetIterator();
|
||||
while (iterator.HasNext()) {
|
||||
net_device_handler *handler = iterator.Next();
|
||||
|
||||
if (handler->type == type) {
|
||||
// found it
|
||||
iterator.Remove();
|
||||
delete handler;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
register_device_monitor(struct net_device *device,
|
||||
net_receive_func receiveFunc, void *cookie)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
// Add new monitor
|
||||
|
||||
net_device_monitor *monitor = new (std::nothrow) net_device_monitor;
|
||||
if (monitor == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
monitor->func = receiveFunc;
|
||||
monitor->cookie = cookie;
|
||||
interface->monitor_funcs.Add(monitor);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
unregister_device_monitor(struct net_device *device,
|
||||
net_receive_func receiveFunc, void *cookie)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
|
||||
// find device interface for this device
|
||||
net_device_interface *interface = find_device_interface(device->name);
|
||||
if (interface == NULL)
|
||||
return ENODEV;
|
||||
|
||||
// search for the monitor
|
||||
|
||||
DeviceMonitorList::Iterator iterator = interface->monitor_funcs.GetIterator();
|
||||
while (iterator.HasNext()) {
|
||||
net_device_monitor *monitor = iterator.Next();
|
||||
|
||||
if (monitor->cookie == cookie && monitor->func == receiveFunc) {
|
||||
// found it
|
||||
iterator.Remove();
|
||||
delete monitor;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
This function is called by device modules once their device got
|
||||
physically removed, ie. a USB networking card is unplugged.
|
||||
It is part of the net_manager_module_info API.
|
||||
*/
|
||||
status_t
|
||||
device_removed(net_device *device)
|
||||
{
|
||||
BenaphoreLocker locker(sInterfaceLock);
|
||||
// TODO: all what this function should do is to clear the IFF_UP flag of the interfaces.
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
|
||||
|
||||
status_t
|
||||
init_interfaces()
|
||||
{
|
||||
if (benaphore_init(&sInterfaceLock, "net interfaces") < B_OK)
|
||||
return B_ERROR;
|
||||
|
||||
list_init(&sInterfaces);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
uninit_interfaces()
|
||||
{
|
||||
benaphore_destroy(&sInterfaceLock);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user