they will no longer be unset - instead they are set with defaults. * If B_SOCKET_SET_ALIAS gets an index of -1, it will now try to find the local address, and if that fails, will just use the first address there is. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38053 a95241bf-73f2-0310-859d-f6bbb57e9c96
1552 lines
36 KiB
C++
1552 lines
36 KiB
C++
/*
|
|
* Copyright 2006-2010, Haiku, Inc. All Rights Reserved.
|
|
* Distributed under the terms of the MIT License.
|
|
*
|
|
* Authors:
|
|
* Axel Dörfler, [email protected]
|
|
*/
|
|
|
|
|
|
#include "interfaces.h"
|
|
|
|
#include <net/if_dl.h>
|
|
#include <new>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/sockio.h>
|
|
|
|
#include <KernelExport.h>
|
|
|
|
#include <net_device.h>
|
|
#include <NetUtilities.h>
|
|
|
|
#include "device_interfaces.h"
|
|
#include "domains.h"
|
|
#include "stack_private.h"
|
|
#include "utility.h"
|
|
|
|
|
|
//#define TRACE_INTERFACES
|
|
#ifdef TRACE_INTERFACES
|
|
# define TRACE(x...) dprintf(STACK_DEBUG_PREFIX x)
|
|
#else
|
|
# define TRACE(x...) ;
|
|
#endif
|
|
|
|
|
|
struct AddressHashDefinition {
|
|
typedef const sockaddr* KeyType;
|
|
typedef InterfaceAddress ValueType;
|
|
|
|
AddressHashDefinition()
|
|
{
|
|
}
|
|
|
|
size_t HashKey(const KeyType& key) const
|
|
{
|
|
net_domain* domain = get_domain(key->sa_family);
|
|
if (domain == NULL)
|
|
return 0;
|
|
|
|
return domain->address_module->hash_address(key, false);
|
|
}
|
|
|
|
size_t Hash(InterfaceAddress* address) const
|
|
{
|
|
return address->domain->address_module->hash_address(address->local, false);
|
|
}
|
|
|
|
bool Compare(const KeyType& key, InterfaceAddress* address) const
|
|
{
|
|
if (address->local == NULL)
|
|
return key->sa_family == AF_UNSPEC;
|
|
|
|
if (address->local->sa_family != key->sa_family)
|
|
return false;
|
|
|
|
return address->domain->address_module->equal_addresses(key,
|
|
address->local);
|
|
}
|
|
|
|
InterfaceAddress*& GetLink(InterfaceAddress* address) const
|
|
{
|
|
return address->HashTableLink();
|
|
}
|
|
};
|
|
|
|
typedef BOpenHashTable<AddressHashDefinition, true, true> AddressTable;
|
|
|
|
|
|
static mutex sLock;
|
|
static InterfaceList sInterfaces;
|
|
static mutex sHashLock;
|
|
static AddressTable sAddressTable;
|
|
static uint32 sInterfaceIndex;
|
|
|
|
|
|
#if ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
static int
|
|
dump_interface(int argc, char** argv)
|
|
{
|
|
if (argc != 2) {
|
|
kprintf("usage: %s [address]\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
Interface* interface = (Interface*)parse_expression(argv[1]);
|
|
interface->Dump();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_interfaces(int argc, char** argv)
|
|
{
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
kprintf("%p %s\n", interface, interface->name);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_local(int argc, char** argv)
|
|
{
|
|
AddressTable::Iterator iterator = sAddressTable.GetIterator();
|
|
size_t i = 0;
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
address->Dump(++i);
|
|
dprintf(" hash: %lu\n",
|
|
address->domain->address_module->hash_address(address->local,
|
|
false));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
dump_route(int argc, char** argv)
|
|
{
|
|
if (argc != 2) {
|
|
kprintf("usage: %s [address]\n", argv[0]);
|
|
return 0;
|
|
}
|
|
|
|
net_route* route = (net_route*)parse_expression(argv[1]);
|
|
kprintf("destination: %p\n", route->destination);
|
|
kprintf("mask: %p\n", route->mask);
|
|
kprintf("gateway: %p\n", route->gateway);
|
|
kprintf("flags: %" B_PRIx32 "\n", route->flags);
|
|
kprintf("mtu: %" B_PRIu32 "\n", route->mtu);
|
|
kprintf("interface address: %p\n", route->interface_address);
|
|
|
|
if (route->interface_address != NULL) {
|
|
((InterfaceAddress*)route->interface_address)->Dump();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
#endif // ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
InterfaceAddress::InterfaceAddress()
|
|
{
|
|
_Init(NULL, NULL);
|
|
}
|
|
|
|
|
|
InterfaceAddress::InterfaceAddress(net_interface* netInterface,
|
|
net_domain* netDomain)
|
|
{
|
|
_Init(netInterface, netDomain);
|
|
}
|
|
|
|
|
|
InterfaceAddress::~InterfaceAddress()
|
|
{
|
|
TRACE("InterfaceAddress %p: destructor\n", this);
|
|
|
|
if (interface != NULL && (flags & IFAF_DIRECT_ADDRESS) == 0)
|
|
((Interface*)interface)->ReleaseReference();
|
|
}
|
|
|
|
|
|
status_t
|
|
InterfaceAddress::SetTo(const ifaliasreq& request)
|
|
{
|
|
status_t status = SetLocal((const sockaddr*)&request.ifra_addr);
|
|
if (status == B_OK)
|
|
status = SetDestination((const sockaddr*)&request.ifra_broadaddr);
|
|
if (status == B_OK)
|
|
status = SetMask((const sockaddr*)&request.ifra_mask);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
InterfaceAddress::SetLocal(const sockaddr* to)
|
|
{
|
|
return Set(&local, to);
|
|
}
|
|
|
|
|
|
status_t
|
|
InterfaceAddress::SetDestination(const sockaddr* to)
|
|
{
|
|
return Set(&destination, to);
|
|
}
|
|
|
|
|
|
status_t
|
|
InterfaceAddress::SetMask(const sockaddr* to)
|
|
{
|
|
return Set(&mask, to);
|
|
}
|
|
|
|
|
|
sockaddr**
|
|
InterfaceAddress::AddressFor(int32 option)
|
|
{
|
|
switch (option) {
|
|
case SIOCSIFADDR:
|
|
case SIOCGIFADDR:
|
|
case SIOCDIFADDR:
|
|
return &local;
|
|
|
|
case SIOCSIFNETMASK:
|
|
case SIOCGIFNETMASK:
|
|
return &mask;
|
|
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCSIFDSTADDR:
|
|
case SIOCGIFBRDADDR:
|
|
case SIOCGIFDSTADDR:
|
|
return &destination;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*! Adds the default routes that every interface address needs, ie. the local
|
|
host route, and one for the subnet (if set).
|
|
*/
|
|
void
|
|
InterfaceAddress::AddDefaultRoutes(int32 option)
|
|
{
|
|
net_route route;
|
|
route.destination = local;
|
|
route.gateway = NULL;
|
|
route.interface_address = this;
|
|
|
|
if (mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) {
|
|
route.mask = mask;
|
|
route.flags = 0;
|
|
add_route(domain, &route);
|
|
}
|
|
|
|
if (option == SIOCSIFADDR) {
|
|
route.mask = NULL;
|
|
route.flags = RTF_LOCAL | RTF_HOST;
|
|
add_route(domain, &route);
|
|
}
|
|
}
|
|
|
|
|
|
/*! Removes the default routes as set by AddDefaultRoutes() again. */
|
|
void
|
|
InterfaceAddress::RemoveDefaultRoutes(int32 option)
|
|
{
|
|
net_route route;
|
|
route.destination = local;
|
|
route.gateway = NULL;
|
|
route.interface_address = this;
|
|
|
|
if (mask != NULL && (option == SIOCSIFNETMASK || option == SIOCSIFADDR)) {
|
|
route.mask = mask;
|
|
route.flags = 0;
|
|
remove_route(domain, &route);
|
|
}
|
|
|
|
if (option == SIOCSIFADDR) {
|
|
route.mask = NULL;
|
|
route.flags = RTF_LOCAL | RTF_HOST;
|
|
remove_route(domain, &route);
|
|
}
|
|
}
|
|
|
|
|
|
#if ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
void
|
|
InterfaceAddress::Dump(size_t index, bool hideInterface)
|
|
{
|
|
if (index)
|
|
kprintf("%2zu. ", index);
|
|
else
|
|
kprintf(" ");
|
|
|
|
if (!hideInterface) {
|
|
kprintf("interface: %p (%s)\n ", interface,
|
|
interface != NULL ? interface->name : "-");
|
|
}
|
|
|
|
kprintf("domain: %p (family %u)\n", domain,
|
|
domain != NULL ? domain->family : AF_UNSPEC);
|
|
|
|
char buffer[64];
|
|
if (local != NULL && domain != NULL) {
|
|
domain->address_module->print_address_buffer(local, buffer,
|
|
sizeof(buffer), false);
|
|
} else
|
|
strcpy(buffer, "-");
|
|
kprintf(" local: %s\n", buffer);
|
|
|
|
if (mask != NULL && domain != NULL) {
|
|
domain->address_module->print_address_buffer(mask, buffer,
|
|
sizeof(buffer), false);
|
|
} else
|
|
strcpy(buffer, "-");
|
|
kprintf(" mask: %s\n", buffer);
|
|
|
|
if (destination != NULL && domain != NULL) {
|
|
domain->address_module->print_address_buffer(destination, buffer,
|
|
sizeof(buffer), false);
|
|
} else
|
|
strcpy(buffer, "-");
|
|
kprintf(" destination: %s\n", buffer);
|
|
|
|
kprintf(" ref count: %" B_PRId32 "\n", CountReferences());
|
|
}
|
|
|
|
|
|
#endif // ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
/*static*/ status_t
|
|
InterfaceAddress::Set(sockaddr** _address, const sockaddr* to)
|
|
{
|
|
sockaddr* address = *_address;
|
|
|
|
if (to == NULL || to->sa_family == AF_UNSPEC) {
|
|
// Clear address
|
|
free(address);
|
|
*_address = NULL;
|
|
return B_OK;
|
|
}
|
|
|
|
// Set address
|
|
|
|
size_t size = max_c(to->sa_len, sizeof(sockaddr));
|
|
if (size > sizeof(sockaddr_storage))
|
|
size = sizeof(sockaddr_storage);
|
|
|
|
address = Prepare(_address, size);
|
|
if (address == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
memcpy(address, to, size);
|
|
address->sa_len = size;
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/*static*/ sockaddr*
|
|
InterfaceAddress::Prepare(sockaddr** _address, size_t size)
|
|
{
|
|
size = max_c(size, sizeof(sockaddr));
|
|
if (size > sizeof(sockaddr_storage))
|
|
size = sizeof(sockaddr_storage);
|
|
|
|
sockaddr* address = *_address;
|
|
|
|
if (address == NULL || size > address->sa_len) {
|
|
address = (sockaddr*)realloc(address, size);
|
|
if (address == NULL)
|
|
return NULL;
|
|
}
|
|
|
|
address->sa_len = size;
|
|
|
|
*_address = address;
|
|
return address;
|
|
}
|
|
|
|
|
|
void
|
|
InterfaceAddress::_Init(net_interface* netInterface, net_domain* netDomain)
|
|
{
|
|
TRACE("InterfaceAddress %p: init interface %p, domain %p\n", this,
|
|
netInterface, netDomain);
|
|
|
|
interface = netInterface;
|
|
domain = netDomain;
|
|
local = NULL;
|
|
destination = NULL;
|
|
mask = NULL;
|
|
flags = 0;
|
|
|
|
if (interface != NULL)
|
|
((Interface*)interface)->AcquireReference();
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
Interface::Interface(const char* interfaceName,
|
|
net_device_interface* deviceInterface)
|
|
{
|
|
TRACE("Interface %p: new \"%s\", device interface %p\n", this,
|
|
interfaceName, deviceInterface);
|
|
|
|
int written = strlcpy(name, interfaceName, IF_NAMESIZE);
|
|
memset(name + written, 0, IF_NAMESIZE - written);
|
|
// Clear remaining space
|
|
|
|
device = deviceInterface->device;
|
|
|
|
index = ++sInterfaceIndex;
|
|
flags = 0;
|
|
type = 0;
|
|
mtu = deviceInterface->device->mtu;
|
|
metric = 0;
|
|
|
|
fDeviceInterface = acquire_device_interface(deviceInterface);
|
|
|
|
recursive_lock_init(&fLock, name);
|
|
|
|
// 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(gNetStackInterfaceModule.info.name, &module);
|
|
}
|
|
|
|
|
|
Interface::~Interface()
|
|
{
|
|
TRACE("Interface %p: destructor\n", this);
|
|
|
|
put_device_interface(fDeviceInterface);
|
|
|
|
// Uninitialize the domain datalink protocols
|
|
|
|
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
|
|
while (domain_datalink* datalink = iterator.Next()) {
|
|
put_domain_datalink_protocols(this, datalink->domain);
|
|
}
|
|
|
|
// Free domain datalink objects
|
|
|
|
domain_datalink* next = fDatalinkTable.Clear(true);
|
|
while (next != NULL) {
|
|
domain_datalink* datalink = next;
|
|
next = next->hash_link;
|
|
|
|
delete datalink;
|
|
}
|
|
|
|
recursive_lock_destroy(&fLock);
|
|
|
|
// Release reference of the stack - at this point, our stack may be unloaded
|
|
// if no other interfaces or sockets are left
|
|
put_module(gNetStackInterfaceModule.info.name);
|
|
}
|
|
|
|
|
|
/*! Returns a reference to the first InterfaceAddress that is from the same
|
|
as the specified \a family.
|
|
*/
|
|
InterfaceAddress*
|
|
Interface::FirstForFamily(int family)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
InterfaceAddress* address = _FirstForFamily(family);
|
|
if (address != NULL) {
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Returns a reference to the first unconfigured address of this interface
|
|
for the specified \a family.
|
|
*/
|
|
InterfaceAddress*
|
|
Interface::FirstUnconfiguredForFamily(int family)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (address->domain->family == family
|
|
&& (address->local == NULL
|
|
// TODO: this has to be solved differently!!
|
|
|| (flags & IFF_CONFIGURING) != 0)) {
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Returns a reference to the InterfaceAddress that has the specified
|
|
\a destination address.
|
|
*/
|
|
InterfaceAddress*
|
|
Interface::AddressForDestination(net_domain* domain,
|
|
const sockaddr* destination)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
if ((device->flags & IFF_BROADCAST) == 0) {
|
|
// The device does not support broadcasting
|
|
return NULL;
|
|
}
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (address->domain == domain
|
|
&& address->destination != NULL
|
|
&& domain->address_module->equal_addresses(address->destination,
|
|
destination)) {
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Returns a reference to the InterfaceAddress that has the specified
|
|
\a local address.
|
|
*/
|
|
InterfaceAddress*
|
|
Interface::AddressForLocal(net_domain* domain, const sockaddr* local)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (address->domain == domain
|
|
&& address->local != NULL
|
|
&& domain->address_module->equal_addresses(address->local, local)) {
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
Interface::AddAddress(InterfaceAddress* address)
|
|
{
|
|
net_domain* domain = address->domain;
|
|
if (domain == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
RecursiveLocker locker(fLock);
|
|
fAddresses.Add(address);
|
|
locker.Unlock();
|
|
|
|
MutexLocker hashLocker(sHashLock);
|
|
sAddressTable.Insert(address);
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
Interface::RemoveAddress(InterfaceAddress* address)
|
|
{
|
|
net_domain* domain = address->domain;
|
|
if (domain == NULL)
|
|
return;
|
|
|
|
RecursiveLocker locker(fLock);
|
|
|
|
fAddresses.Remove(address);
|
|
address->GetDoublyLinkedListLink()->next = NULL;
|
|
|
|
locker.Unlock();
|
|
|
|
MutexLocker hashLocker(sHashLock);
|
|
sAddressTable.Remove(address);
|
|
}
|
|
|
|
|
|
bool
|
|
Interface::GetNextAddress(InterfaceAddress** _address)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
InterfaceAddress* address = *_address;
|
|
if (address == NULL) {
|
|
// get first address
|
|
address = fAddresses.First();
|
|
} else {
|
|
// get next, if possible
|
|
InterfaceAddress* next = fAddresses.GetNext(address);
|
|
address->ReleaseReference();
|
|
address = next;
|
|
}
|
|
|
|
*_address = address;
|
|
|
|
if (address == NULL)
|
|
return false;
|
|
|
|
address->AcquireReference();
|
|
return true;
|
|
}
|
|
|
|
|
|
InterfaceAddress*
|
|
Interface::AddressAt(size_t index)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
size_t i = 0;
|
|
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (i++ == index) {
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
size_t
|
|
Interface::CountAddresses()
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
return fAddresses.Count();
|
|
}
|
|
|
|
|
|
void
|
|
Interface::RemoveAddresses()
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
while (InterfaceAddress* address = fAddresses.RemoveHead()) {
|
|
address->ReleaseReference();
|
|
}
|
|
}
|
|
|
|
|
|
/*! This is called in order to call the correct methods of the datalink
|
|
protocols, ie. it will translate address changes to
|
|
net_datalink_protocol::change_address(), and IFF_UP changes to
|
|
net_datalink_protocol::interface_up(), and interface_down().
|
|
|
|
Everything else is passed unchanged to net_datalink_protocol::control().
|
|
*/
|
|
status_t
|
|
Interface::Control(net_domain* domain, int32 option, ifreq& request,
|
|
ifreq* userRequest, size_t length)
|
|
{
|
|
switch (option) {
|
|
case SIOCSIFFLAGS:
|
|
{
|
|
if (length != sizeof(ifreq))
|
|
return B_BAD_VALUE;
|
|
|
|
uint32 requestFlags = request.ifr_flags;
|
|
uint32 oldFlags = flags;
|
|
status_t status = B_OK;
|
|
|
|
request.ifr_flags &= ~(IFF_UP | IFF_LINK | IFF_BROADCAST);
|
|
|
|
if ((requestFlags & IFF_UP) != (flags & IFF_UP)) {
|
|
if ((requestFlags & IFF_UP) != 0)
|
|
status = _SetUp();
|
|
else
|
|
SetDown();
|
|
}
|
|
|
|
if (status == B_OK) {
|
|
// TODO: maybe allow deleting IFF_BROADCAST on the interface
|
|
// level?
|
|
flags &= IFF_UP | IFF_LINK | IFF_BROADCAST;
|
|
flags |= request.ifr_flags;
|
|
}
|
|
|
|
if (oldFlags != flags)
|
|
notify_interface_changed(this, oldFlags, flags);
|
|
|
|
return status;
|
|
}
|
|
|
|
case B_SOCKET_SET_ALIAS:
|
|
{
|
|
if (length != sizeof(ifaliasreq))
|
|
return B_BAD_VALUE;
|
|
|
|
RecursiveLocker locker(fLock);
|
|
|
|
ifaliasreq aliasRequest;
|
|
if (user_memcpy(&aliasRequest, userRequest, sizeof(ifaliasreq))
|
|
!= B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
InterfaceAddress* address = NULL;
|
|
if (aliasRequest.ifra_index < 0) {
|
|
if (!domain->address_module->is_empty_address(
|
|
(const sockaddr*)&aliasRequest.ifra_addr, false)) {
|
|
// Find first address that matches the local address
|
|
address = AddressForLocal(domain,
|
|
(const sockaddr*)&aliasRequest.ifra_addr);
|
|
}
|
|
if (address == NULL) {
|
|
// Find first address for family
|
|
address = FirstForFamily(domain->family);
|
|
}
|
|
} else
|
|
address = AddressAt(aliasRequest.ifra_index);
|
|
|
|
if (address == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
status_t status = B_OK;
|
|
address->AcquireReference();
|
|
// _ChangeAddress() currently unlocks, so we need another
|
|
// reference to make sure "address" is not going away.
|
|
|
|
if (!domain->address_module->equal_addresses(
|
|
(sockaddr*)&aliasRequest.ifra_addr, address->local)) {
|
|
status = _ChangeAddress(locker, address, SIOCSIFADDR,
|
|
address->local, (sockaddr*)&aliasRequest.ifra_addr);
|
|
}
|
|
|
|
if (status == B_OK && !domain->address_module->equal_addresses(
|
|
(sockaddr*)&aliasRequest.ifra_mask, address->mask)
|
|
&& !domain->address_module->is_empty_address(
|
|
(sockaddr*)&aliasRequest.ifra_mask, false)) {
|
|
status = _ChangeAddress(locker, address, SIOCSIFNETMASK,
|
|
address->mask, (sockaddr*)&aliasRequest.ifra_mask);
|
|
}
|
|
|
|
if (status == B_OK && !domain->address_module->equal_addresses(
|
|
(sockaddr*)&aliasRequest.ifra_destination,
|
|
address->destination)
|
|
&& !domain->address_module->is_empty_address(
|
|
(sockaddr*)&aliasRequest.ifra_destination, false)) {
|
|
status = _ChangeAddress(locker, address,
|
|
(domain->address_module->flags
|
|
& NET_ADDRESS_MODULE_FLAG_BROADCAST_ADDRESS) != 0
|
|
? SIOCSIFBRDADDR : SIOCSIFDSTADDR,
|
|
address->destination,
|
|
(sockaddr*)&aliasRequest.ifra_destination);
|
|
}
|
|
|
|
address->ReleaseReference();
|
|
return status;
|
|
}
|
|
|
|
case SIOCSIFADDR:
|
|
case SIOCSIFNETMASK:
|
|
case SIOCSIFBRDADDR:
|
|
case SIOCSIFDSTADDR:
|
|
case SIOCDIFADDR:
|
|
{
|
|
if (length != sizeof(ifreq))
|
|
return B_BAD_VALUE;
|
|
|
|
RecursiveLocker locker(fLock);
|
|
|
|
InterfaceAddress* address = NULL;
|
|
sockaddr_storage newAddress;
|
|
|
|
size_t size = max_c(request.ifr_addr.sa_len, sizeof(sockaddr));
|
|
if (size > sizeof(sockaddr_storage))
|
|
size = sizeof(sockaddr_storage);
|
|
|
|
if (user_memcpy(&newAddress, &userRequest->ifr_addr, size) != B_OK)
|
|
return B_BAD_ADDRESS;
|
|
|
|
if (option == SIOCDIFADDR) {
|
|
// Find referring address - we can't use the hash, as another
|
|
// interface might use the same address.
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while ((address = iterator.Next()) != NULL) {
|
|
if (address->domain == domain
|
|
&& domain->address_module->equal_addresses(
|
|
address->local, (sockaddr*)&newAddress))
|
|
break;
|
|
}
|
|
|
|
if (address == NULL)
|
|
return B_BAD_VALUE;
|
|
} else {
|
|
// Just use the first address for this family
|
|
address = _FirstForFamily(domain->family);
|
|
if (address == NULL) {
|
|
// Create new on the fly
|
|
address = new(std::nothrow) InterfaceAddress(this, domain);
|
|
if (address == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
status_t status = AddAddress(address);
|
|
if (status != B_OK)
|
|
return status;
|
|
|
|
// Note, even if setting the address failed, the empty
|
|
// address added here will still be added to the interface.
|
|
}
|
|
}
|
|
|
|
return _ChangeAddress(locker, address, option,
|
|
*address->AddressFor(option),
|
|
option != SIOCDIFADDR ? (sockaddr*)&newAddress : NULL);
|
|
}
|
|
|
|
default:
|
|
// pass the request into the datalink protocol stack
|
|
domain_datalink* datalink = DomainDatalink(domain->family);
|
|
if (datalink->first_info != NULL) {
|
|
return datalink->first_info->control(
|
|
datalink->first_protocol, option, userRequest, length);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return B_BAD_VALUE;
|
|
}
|
|
|
|
|
|
void
|
|
Interface::SetDown()
|
|
{
|
|
if ((flags & IFF_UP) == 0)
|
|
return;
|
|
|
|
RecursiveLocker locker(fLock);
|
|
|
|
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
|
|
while (domain_datalink* datalink = iterator.Next()) {
|
|
datalink->first_info->interface_down(datalink->first_protocol);
|
|
}
|
|
|
|
flags &= ~IFF_UP;
|
|
}
|
|
|
|
|
|
/*! Called when a device lost its IFF_UP status. We will invalidate all
|
|
interface routes here.
|
|
*/
|
|
void
|
|
Interface::WentDown()
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (address->domain != NULL)
|
|
invalidate_routes(address->domain, this);
|
|
}
|
|
}
|
|
|
|
|
|
status_t
|
|
Interface::CreateDomainDatalinkIfNeeded(net_domain* domain)
|
|
{
|
|
RecursiveLocker locker(fLock);
|
|
|
|
if (fDatalinkTable.Lookup(domain->family) != NULL)
|
|
return B_OK;
|
|
|
|
TRACE("Interface %p: create domain datalink for domain %p\n", this, domain);
|
|
|
|
domain_datalink* datalink = new(std::nothrow) domain_datalink;
|
|
if (datalink == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
datalink->domain = domain;
|
|
|
|
// setup direct route for bound devices
|
|
datalink->direct_route.destination = NULL;
|
|
datalink->direct_route.mask = NULL;
|
|
datalink->direct_route.gateway = NULL;
|
|
datalink->direct_route.flags = 0;
|
|
datalink->direct_route.mtu = 0;
|
|
datalink->direct_route.interface_address = &datalink->direct_address;
|
|
datalink->direct_route.ref_count = 1;
|
|
// make sure this doesn't get deleted accidently
|
|
|
|
// provide its link back to the interface
|
|
datalink->direct_address.local = NULL;
|
|
datalink->direct_address.destination = NULL;
|
|
datalink->direct_address.mask = NULL;
|
|
datalink->direct_address.domain = domain;
|
|
datalink->direct_address.interface = this;
|
|
datalink->direct_address.flags = IFAF_DIRECT_ADDRESS;
|
|
|
|
fDatalinkTable.Insert(datalink);
|
|
|
|
status_t status = get_domain_datalink_protocols(this, domain);
|
|
if (status == B_OK)
|
|
return B_OK;
|
|
|
|
fDatalinkTable.Remove(datalink);
|
|
delete datalink;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
domain_datalink*
|
|
Interface::DomainDatalink(uint8 family)
|
|
{
|
|
// Note: domain datalinks cannot be removed while the interface is alive,
|
|
// since this would require us either to hold the lock while calling this
|
|
// function, or introduce reference counting for the domain_datalink
|
|
// structure.
|
|
RecursiveLocker locker(fLock);
|
|
return fDatalinkTable.Lookup(family);
|
|
}
|
|
|
|
|
|
#if ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
void
|
|
Interface::Dump() const
|
|
{
|
|
kprintf("name: %s\n", name);
|
|
kprintf("device: %p\n", device);
|
|
kprintf("device_interface: %p\n", fDeviceInterface);
|
|
kprintf("index: %" B_PRIu32 "\n", index);
|
|
kprintf("flags: %#" B_PRIx32 "\n", flags);
|
|
kprintf("type: %u\n", type);
|
|
kprintf("mtu: %" B_PRIu32 "\n", mtu);
|
|
kprintf("metric: %" B_PRIu32 "\n", metric);
|
|
|
|
kprintf("datalink protocols:\n");
|
|
|
|
DatalinkTable::Iterator datalinkIterator = fDatalinkTable.GetIterator();
|
|
size_t i = 0;
|
|
while (domain_datalink* datalink = datalinkIterator.Next()) {
|
|
kprintf("%2zu. domain: %p\n", ++i, datalink->domain);
|
|
kprintf(" first_protocol: %p\n", datalink->first_protocol);
|
|
kprintf(" first_info: %p\n", datalink->first_info);
|
|
kprintf(" direct_route: %p\n", &datalink->direct_route);
|
|
}
|
|
|
|
kprintf("addresses:\n");
|
|
|
|
AddressList::ConstIterator iterator = fAddresses.GetIterator();
|
|
i = 0;
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
address->Dump(++i, true);
|
|
}
|
|
}
|
|
|
|
|
|
#endif // ENABLE_DEBUGGER_COMMANDS
|
|
|
|
|
|
status_t
|
|
Interface::_SetUp()
|
|
{
|
|
status_t status = up_device_interface(fDeviceInterface);
|
|
if (status != B_OK)
|
|
return status;
|
|
|
|
// Propagate flag to all datalink protocols
|
|
|
|
RecursiveLocker locker(fLock);
|
|
|
|
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
|
|
while (domain_datalink* datalink = iterator.Next()) {
|
|
status = datalink->first_info->interface_up(datalink->first_protocol);
|
|
if (status != B_OK) {
|
|
// Revert "up" status
|
|
DatalinkTable::Iterator secondIterator
|
|
= fDatalinkTable.GetIterator();
|
|
while (secondIterator.HasNext()) {
|
|
domain_datalink* secondDatalink = secondIterator.Next();
|
|
if (secondDatalink == NULL || secondDatalink == datalink)
|
|
break;
|
|
|
|
secondDatalink->first_info->interface_down(
|
|
secondDatalink->first_protocol);
|
|
}
|
|
|
|
down_device_interface(fDeviceInterface);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
// Add default routes for the existing addresses
|
|
|
|
AddressList::Iterator addressIterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = addressIterator.Next()) {
|
|
address->AddDefaultRoutes(SIOCSIFADDR);
|
|
}
|
|
|
|
flags |= IFF_UP;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
InterfaceAddress*
|
|
Interface::_FirstForFamily(int family)
|
|
{
|
|
ASSERT_LOCKED_RECURSIVE(&fLock);
|
|
|
|
AddressList::Iterator iterator = fAddresses.GetIterator();
|
|
while (InterfaceAddress* address = iterator.Next()) {
|
|
if (address->domain != NULL && address->domain->family == family)
|
|
return address;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
status_t
|
|
Interface::_ChangeAddress(RecursiveLocker& locker, InterfaceAddress* address,
|
|
int32 option, const sockaddr* originalAddress,
|
|
const sockaddr* requestedAddress)
|
|
{
|
|
// Copy old address
|
|
sockaddr_storage oldAddress;
|
|
if (address->domain->address_module->set_to((sockaddr*)&oldAddress,
|
|
originalAddress) != B_OK)
|
|
oldAddress.ss_family = AF_UNSPEC;
|
|
|
|
// Copy new address (this also makes sure that sockaddr::sa_len is set
|
|
// correctly)
|
|
sockaddr_storage newAddress;
|
|
if (address->domain->address_module->set_to((sockaddr*)&newAddress,
|
|
requestedAddress) != B_OK)
|
|
newAddress.ss_family = AF_UNSPEC;
|
|
|
|
// Test if anything changed for real
|
|
if (address->domain->address_module->equal_addresses(
|
|
(sockaddr*)&oldAddress, (sockaddr*)&newAddress)) {
|
|
// Nothing to do
|
|
TRACE(" option %" B_PRId32 " addresses are equal!\n", option);
|
|
return B_OK;
|
|
}
|
|
|
|
// TODO: mark this address busy or call while holding the lock!
|
|
address->AcquireReference();
|
|
locker.Unlock();
|
|
|
|
domain_datalink* datalink = DomainDatalink(address->domain);
|
|
status_t status = datalink->first_protocol->module->change_address(
|
|
datalink->first_protocol, address, option,
|
|
oldAddress.ss_family != AF_UNSPEC ? (sockaddr*)&oldAddress : NULL,
|
|
newAddress.ss_family != AF_UNSPEC ? (sockaddr*)&newAddress : NULL);
|
|
|
|
locker.Lock();
|
|
address->ReleaseReference();
|
|
return status;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
/*! Searches for a specific interface by name.
|
|
You need to have the interface list's lock hold when calling this function.
|
|
*/
|
|
static struct Interface*
|
|
find_interface(const char* name)
|
|
{
|
|
ASSERT_LOCKED_MUTEX(&sLock);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
if (!strcmp(interface->name, name))
|
|
return interface;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Searches for a specific interface by index.
|
|
You need to have the interface list's lock hold when calling this function.
|
|
*/
|
|
static struct Interface*
|
|
find_interface(uint32 index)
|
|
{
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
if (interface->index == index)
|
|
return interface;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
status_t
|
|
add_interface(const char* name, net_domain_private* domain,
|
|
const ifaliasreq& request, net_device_interface* deviceInterface)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
if (find_interface(name) != NULL)
|
|
return B_NAME_IN_USE;
|
|
|
|
Interface* interface
|
|
= new(std::nothrow) Interface(name, deviceInterface);
|
|
if (interface == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
sInterfaces.Add(interface);
|
|
interface->AcquireReference();
|
|
// We need another reference to be able to use the interface without
|
|
// holding sLock.
|
|
|
|
locker.Unlock();
|
|
|
|
notify_interface_added(interface);
|
|
add_interface_address(interface, domain, request);
|
|
|
|
interface->ReleaseReference();
|
|
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
/*! Removes the interface from the list, and puts the stack's reference to it.
|
|
*/
|
|
void
|
|
remove_interface(Interface* interface)
|
|
{
|
|
interface->SetDown();
|
|
interface->RemoveAddresses();
|
|
|
|
MutexLocker locker(sLock);
|
|
sInterfaces.Remove(interface);
|
|
locker.Unlock();
|
|
|
|
notify_interface_removed(interface);
|
|
|
|
interface->ReleaseReference();
|
|
}
|
|
|
|
|
|
/*! This is called whenever a device interface is being removed. We will get
|
|
the corresponding Interface, and remove it.
|
|
*/
|
|
void
|
|
interface_removed_device_interface(net_device_interface* deviceInterface)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
Interface* interface = find_interface(deviceInterface->device->name);
|
|
if (interface != NULL)
|
|
remove_interface(interface);
|
|
}
|
|
|
|
|
|
status_t
|
|
add_interface_address(Interface* interface, net_domain_private* domain,
|
|
const ifaliasreq& request)
|
|
{
|
|
// Make sure the family of the provided addresses is valid
|
|
if ((request.ifra_addr.ss_family != domain->family
|
|
&& request.ifra_addr.ss_family != AF_UNSPEC)
|
|
|| (request.ifra_mask.ss_family != domain->family
|
|
&& request.ifra_mask.ss_family != AF_UNSPEC)
|
|
|| (request.ifra_broadaddr.ss_family != domain->family
|
|
&& request.ifra_broadaddr.ss_family != AF_UNSPEC))
|
|
return B_BAD_VALUE;
|
|
|
|
RecursiveLocker locker(interface->Lock());
|
|
|
|
InterfaceAddress* address
|
|
= new(std::nothrow) InterfaceAddress(interface, domain);
|
|
if (address == NULL)
|
|
return B_NO_MEMORY;
|
|
|
|
status_t status = address->SetTo(request);
|
|
if (status == B_OK)
|
|
status = interface->CreateDomainDatalinkIfNeeded(domain);
|
|
if (status == B_OK)
|
|
status = interface->AddAddress(address);
|
|
|
|
if (status == B_OK && address->local != NULL) {
|
|
// update the datalink protocols
|
|
domain_datalink* datalink = interface->DomainDatalink(domain->family);
|
|
|
|
status = datalink->first_protocol->module->change_address(
|
|
datalink->first_protocol, address, SIOCAIFADDR, NULL,
|
|
address->local);
|
|
if (status != B_OK)
|
|
interface->RemoveAddress(address);
|
|
}
|
|
if (status == B_OK)
|
|
notify_interface_changed(interface);
|
|
else
|
|
delete address;
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
status_t
|
|
update_interface_address(InterfaceAddress* interfaceAddress, int32 option,
|
|
const sockaddr* oldAddress, const sockaddr* newAddress)
|
|
{
|
|
TRACE("%s(address %p, option %" B_PRId32 ", oldAddress %s, newAddress "
|
|
"%s)\n", __FUNCTION__, interfaceAddress, option,
|
|
AddressString(interfaceAddress->domain, oldAddress).Data(),
|
|
AddressString(interfaceAddress->domain, newAddress).Data());
|
|
|
|
MutexLocker locker(sHashLock);
|
|
|
|
// set logical interface address
|
|
sockaddr** _address = interfaceAddress->AddressFor(option);
|
|
if (_address == NULL)
|
|
return B_BAD_VALUE;
|
|
|
|
Interface* interface = (Interface*)interfaceAddress->interface;
|
|
|
|
interfaceAddress->RemoveDefaultRoutes(option);
|
|
|
|
if (option == SIOCDIFADDR) {
|
|
// Remove address, and release its reference (causing our caller to
|
|
// delete it)
|
|
locker.Unlock();
|
|
|
|
invalidate_routes(interfaceAddress);
|
|
|
|
interface->RemoveAddress(interfaceAddress);
|
|
interfaceAddress->ReleaseReference();
|
|
return B_OK;
|
|
}
|
|
|
|
sAddressTable.Remove(interfaceAddress);
|
|
|
|
// Copy new address over
|
|
status_t status = InterfaceAddress::Set(_address, newAddress);
|
|
if (status == B_OK) {
|
|
sockaddr* address = *_address;
|
|
|
|
if (option == SIOCSIFADDR) {
|
|
// Reset netmask and broadcast addresses to defaults
|
|
net_domain* domain = interfaceAddress->domain;
|
|
sockaddr* netmask = NULL;
|
|
const sockaddr* oldNetmask = NULL;
|
|
if (option == SIOCSIFADDR) {
|
|
netmask = InterfaceAddress::Prepare(
|
|
&interfaceAddress->mask, address->sa_len);
|
|
} else {
|
|
oldNetmask = oldAddress;
|
|
netmask = interfaceAddress->mask;
|
|
}
|
|
|
|
// Reset the broadcast address if the address family has
|
|
// such
|
|
sockaddr* broadcast = NULL;
|
|
if ((domain->address_module->flags
|
|
& NET_ADDRESS_MODULE_FLAG_BROADCAST_ADDRESS) != 0) {
|
|
broadcast = InterfaceAddress::Prepare(
|
|
&interfaceAddress->destination, address->sa_len);
|
|
} else
|
|
InterfaceAddress::Set(&interfaceAddress->destination, NULL);
|
|
|
|
domain->address_module->set_to_defaults(netmask, broadcast,
|
|
interfaceAddress->local, oldNetmask);
|
|
}
|
|
|
|
interfaceAddress->AddDefaultRoutes(option);
|
|
notify_interface_changed(interface);
|
|
}
|
|
|
|
sAddressTable.Insert(interfaceAddress);
|
|
return status;
|
|
}
|
|
|
|
|
|
Interface*
|
|
get_interface(net_domain* domain, uint32 index)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
if (index == 0)
|
|
return sInterfaces.First();
|
|
|
|
Interface* interface = find_interface(index);
|
|
if (interface == NULL)
|
|
return NULL;
|
|
|
|
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
|
|
return NULL;
|
|
|
|
interface->AcquireReference();
|
|
return interface;
|
|
}
|
|
|
|
|
|
Interface*
|
|
get_interface(net_domain* domain, const char* name)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
Interface* interface = find_interface(name);
|
|
if (interface == NULL)
|
|
return NULL;
|
|
|
|
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
|
|
return NULL;
|
|
|
|
interface->AcquireReference();
|
|
return interface;
|
|
}
|
|
|
|
|
|
Interface*
|
|
get_interface_for_device(net_domain* domain, uint32 index)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
if (interface->device->index == index) {
|
|
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
|
|
return NULL;
|
|
|
|
interface->AcquireReference();
|
|
return interface;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Returns a reference to an Interface that matches the given \a linkAddress.
|
|
The link address is checked against its hardware address, or its interface
|
|
name, or finally the interface index.
|
|
*/
|
|
Interface*
|
|
get_interface_for_link(net_domain* domain, const sockaddr* _linkAddress)
|
|
{
|
|
sockaddr_dl& linkAddress = *(sockaddr_dl*)_linkAddress;
|
|
|
|
MutexLocker locker(sLock);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
// Test if the hardware address matches, or if the given interface
|
|
// matches, or if at least the index matches.
|
|
if ((linkAddress.sdl_alen == interface->device->address.length
|
|
&& memcmp(LLADDR(&linkAddress), interface->device->address.data,
|
|
linkAddress.sdl_alen) == 0)
|
|
|| (linkAddress.sdl_nlen > 0
|
|
&& !strcmp(interface->name, (const char*)linkAddress.sdl_data))
|
|
|| (linkAddress.sdl_nlen == 0 && linkAddress.sdl_alen == 0
|
|
&& linkAddress.sdl_index == interface->index)) {
|
|
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
|
|
return NULL;
|
|
|
|
interface->AcquireReference();
|
|
return interface;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
InterfaceAddress*
|
|
get_interface_address(const sockaddr* local)
|
|
{
|
|
if (local->sa_family == AF_UNSPEC)
|
|
return NULL;
|
|
|
|
MutexLocker locker(sHashLock);
|
|
|
|
InterfaceAddress* address = sAddressTable.Lookup(local);
|
|
if (address == NULL)
|
|
return NULL;
|
|
|
|
address->AcquireReference();
|
|
return address;
|
|
}
|
|
|
|
|
|
InterfaceAddress*
|
|
get_interface_address_for_destination(net_domain* domain,
|
|
const sockaddr* destination)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
InterfaceAddress* address
|
|
= interface->AddressForDestination(domain, destination);
|
|
if (address != NULL)
|
|
return address;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*! Returns a reference to an InterfaceAddress of the specified \a domain that
|
|
belongs to the interface identified via \a linkAddress. Only the hardware
|
|
address is matched.
|
|
|
|
If \a unconfiguredOnly is set, the interface address must not yet be
|
|
configured, or must currently be in the process of being configured.
|
|
*/
|
|
InterfaceAddress*
|
|
get_interface_address_for_link(net_domain* domain, const sockaddr* address,
|
|
bool unconfiguredOnly)
|
|
{
|
|
sockaddr_dl& linkAddress = *(sockaddr_dl*)address;
|
|
|
|
MutexLocker locker(sLock);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
// Test if the hardware address matches, or if the given interface
|
|
// matches, or if at least the index matches.
|
|
if (linkAddress.sdl_alen == interface->device->address.length
|
|
&& memcmp(LLADDR(&linkAddress), interface->device->address.data,
|
|
linkAddress.sdl_alen) == 0) {
|
|
TRACE(" %s matches\n", interface->name);
|
|
// link address matches
|
|
if (unconfiguredOnly)
|
|
return interface->FirstUnconfiguredForFamily(domain->family);
|
|
|
|
return interface->FirstForFamily(domain->family);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
uint32
|
|
count_interfaces()
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
return sInterfaces.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_interfaces(int family, void* _buffer, size_t* bufferSize)
|
|
{
|
|
MutexLocker locker(sLock);
|
|
|
|
UserBuffer buffer(_buffer, *bufferSize);
|
|
|
|
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
|
|
while (Interface* interface = iterator.Next()) {
|
|
// Copy name
|
|
buffer.Push(interface->name, IF_NAMESIZE);
|
|
|
|
// Copy address
|
|
InterfaceAddress* address = interface->FirstForFamily(family);
|
|
size_t length = 0;
|
|
|
|
if (address != NULL && address->local != NULL) {
|
|
// Actual address
|
|
buffer.Push(address->local, length = address->local->sa_len);
|
|
} else {
|
|
// Empty address
|
|
sockaddr empty;
|
|
empty.sa_len = 2;
|
|
empty.sa_family = AF_UNSPEC;
|
|
buffer.Push(&empty, length = empty.sa_len);
|
|
}
|
|
|
|
if (address != NULL)
|
|
address->ReleaseReference();
|
|
|
|
if (IF_NAMESIZE + length < sizeof(ifreq)) {
|
|
// Make sure at least sizeof(ifreq) bytes are written for each
|
|
// interface for compatibility with other platforms
|
|
buffer.Pad(sizeof(ifreq) - IF_NAMESIZE - length);
|
|
}
|
|
|
|
if (buffer.Status() != B_OK)
|
|
return buffer.Status();
|
|
}
|
|
|
|
*bufferSize = buffer.BytesConsumed();
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
// #pragma mark -
|
|
|
|
|
|
status_t
|
|
init_interfaces()
|
|
{
|
|
mutex_init(&sLock, "net interfaces");
|
|
mutex_init(&sHashLock, "net local addresses");
|
|
|
|
new (&sInterfaces) InterfaceList;
|
|
new (&sAddressTable) AddressTable;
|
|
// static C++ objects are not initialized in the module startup
|
|
|
|
#if ENABLE_DEBUGGER_COMMANDS
|
|
add_debugger_command("net_interface", &dump_interface,
|
|
"Dump the given network interface");
|
|
add_debugger_command("net_interfaces", &dump_interfaces,
|
|
"Dump all network interfaces");
|
|
add_debugger_command("net_local", &dump_local,
|
|
"Dump all local interface addresses");
|
|
add_debugger_command("net_route", &dump_route,
|
|
"Dump the given network route");
|
|
#endif
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
status_t
|
|
uninit_interfaces()
|
|
{
|
|
#if ENABLE_DEBUGGER_COMMANDS
|
|
remove_debugger_command("net_interface", &dump_interface);
|
|
#endif
|
|
|
|
mutex_destroy(&sLock);
|
|
mutex_destroy(&sHashLock);
|
|
return B_OK;
|
|
}
|
|
|