#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"
#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, false> AddressTable;
static recursive_lock sLock;
static InterfaceList sInterfaces;
static mutex sHashLock;
static AddressTable sAddressTable;
static uint32 sInterfaceIndex;
#if 0
void
dump_interface_refs(void)
{
RecursiveLocker locker(sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
dprintf("%p: %s, %ld\n", interface, interface->name,
interface->CountReferences());
}
}
#endif
#if ENABLE_DEBUGGER_COMMANDS
static int
dump_interface(int argc, char** argv)
{
if (argc != 2) {
kprintf("usage: %s [name|address]\n", argv[0]);
return 0;
}
Interface* interface = NULL;
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while ((interface = iterator.Next()) != NULL) {
if (!strcmp(argv[1], interface->name))
break;
}
if (interface == NULL)
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: %" B_PRIu32 "\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
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;
}
}
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);
}
}
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);
}
}
bool
InterfaceAddress::LocalIsDefined() const
{
return local != NULL && local->sa_family != AF_UNSPEC;
}
#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
status_t
InterfaceAddress::Set(sockaddr** _address, const sockaddr* to)
{
sockaddr* address = *_address;
if (to == NULL || to->sa_family == AF_UNSPEC) {
free(address);
*_address = NULL;
return B_OK;
}
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;
}
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) {
sockaddr* resized = (sockaddr*)realloc(address, size);
if (resized == NULL) {
free(address);
return NULL;
}
address = resized;
}
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();
}
Interface::Interface(const char* interfaceName,
net_device_interface* deviceInterface)
:
fBusy(false)
{
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);
device = deviceInterface->device;
index = ++sInterfaceIndex;
flags = 0;
type = 0;
metric = 0;
fDeviceInterface = acquire_device_interface(deviceInterface);
recursive_lock_init(&fLock, name);
module_info* module;
get_module(gNetStackInterfaceModule.info.name, &module);
}
Interface::~Interface()
{
TRACE("Interface %p: destructor\n", this);
ASSERT((flags & IFF_UP) == 0);
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
while (domain_datalink* datalink = iterator.Next()) {
put_domain_datalink_protocols(this, datalink->domain);
}
domain_datalink* next = fDatalinkTable.Clear(true);
while (next != NULL) {
domain_datalink* datalink = next;
next = next->hash_link;
delete datalink;
}
put_device_interface(fDeviceInterface);
recursive_lock_destroy(&fLock);
put_module(gNetStackInterfaceModule.info.name);
}
InterfaceAddress*
Interface::FirstForFamily(int family)
{
RecursiveLocker locker(fLock);
InterfaceAddress* address = _FirstForFamily(family);
if (address != NULL) {
address->AcquireReference();
return address;
}
return NULL;
}
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
|| (flags & IFF_CONFIGURING) != 0)) {
address->AcquireReference();
return address;
}
}
return NULL;
}
InterfaceAddress*
Interface::AddressForDestination(net_domain* domain,
const sockaddr* destination)
{
RecursiveLocker locker(fLock);
if ((device->flags & IFF_BROADCAST) == 0) {
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;
}
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();
if (address->LocalIsDefined()) {
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();
if (address->LocalIsDefined()) {
MutexLocker hashLocker(sHashLock);
sAddressTable.Remove(address);
}
}
bool
Interface::GetNextAddress(InterfaceAddress** _address)
{
RecursiveLocker locker(fLock);
InterfaceAddress* address = *_address;
if (address == NULL) {
address = fAddresses.First();
} else {
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;
}
int32
Interface::IndexOfAddress(InterfaceAddress* address)
{
if (address == NULL)
return -1;
RecursiveLocker locker(fLock);
AddressList::Iterator iterator = fAddresses.GetIterator();
int32 index = 0;
while (iterator.HasNext()) {
if (address == iterator.Next())
return index;
index++;
}
return -1;
}
size_t
Interface::CountAddresses()
{
RecursiveLocker locker(fLock);
return fAddresses.Count();
}
void
Interface::RemoveAddresses()
{
RecursiveLocker locker(fLock);
while (InterfaceAddress* address = fAddresses.RemoveHead()) {
locker.Unlock();
if (address->LocalIsDefined()) {
MutexLocker hashLocker(sHashLock);
sAddressTable.Remove(address);
}
address->ReleaseReference();
locker.Lock();
}
}
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) {
flags &= IFF_UP | IFF_LINK | IFF_BROADCAST;
flags |= request.ifr_flags;
}
if (oldFlags != flags) {
TRACE("Interface %p: flags changed from %" B_PRIx32 " to %"
B_PRIx32 "\n", this, 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)) {
address = AddressForLocal(domain,
(const sockaddr*)&aliasRequest.ifra_addr);
}
if (address == NULL) {
address = FirstForFamily(domain->family);
}
if (address == NULL) {
address = new(std::nothrow) InterfaceAddress(this, domain);
if (address == NULL)
return B_NO_MEMORY;
status_t status = AddAddress(address);
if (status != B_OK) {
delete address;
return status;
}
address->AcquireReference();
}
} else
address = AddressAt(aliasRequest.ifra_index);
if (address == NULL)
return B_BAD_VALUE;
status_t status = B_OK;
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) {
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 {
address = _FirstForFamily(domain->family);
if (address == NULL) {
address = new(std::nothrow) InterfaceAddress(this, domain);
if (address == NULL)
return B_NO_MEMORY;
status_t status = AddAddress(address);
if (status != B_OK) {
delete address;
return status;
}
}
}
return _ChangeAddress(locker, address, option,
*address->AddressFor(option),
option != SIOCDIFADDR ? (sockaddr*)&newAddress : NULL);
}
default:
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 interfacesLocker(sLock);
if (IsBusy())
return;
SetBusy(true);
interfacesLocker.Unlock();
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
while (domain_datalink* datalink = iterator.Next()) {
datalink->first_info->interface_down(datalink->first_protocol);
}
flags &= ~IFF_UP;
SetBusy(false);
}
void
Interface::WentDown()
{
TRACE("Interface %p: went down\n", this);
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->first_protocol = NULL;
datalink->first_info = NULL;
datalink->domain = domain;
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;
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)
{
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("metric: %" B_PRIu32 "\n", metric);
kprintf("ref count: %" B_PRId32 "\n", CountReferences());
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
status_t
Interface::_SetUp()
{
status_t status = up_device_interface(fDeviceInterface);
if (status != B_OK)
return status;
RecursiveLocker interfacesLocker(sLock);
SetBusy(true);
interfacesLocker.Unlock();
DatalinkTable::Iterator iterator = fDatalinkTable.GetIterator();
while (domain_datalink* datalink = iterator.Next()) {
status = datalink->first_info->interface_up(datalink->first_protocol);
if (status != B_OK) {
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);
SetBusy(false);
return status;
}
}
AddressList::Iterator addressIterator = fAddresses.GetIterator();
while (InterfaceAddress* address = addressIterator.Next()) {
address->AddDefaultRoutes(SIOCSIFADDR);
}
flags |= IFF_UP;
SetBusy(false);
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)
{
sockaddr_storage oldAddress;
if (address->domain->address_module->set_to((sockaddr*)&oldAddress,
originalAddress) != B_OK)
oldAddress.ss_family = AF_UNSPEC;
sockaddr_storage newAddress;
if (address->domain->address_module->set_to((sockaddr*)&newAddress,
requestedAddress) != B_OK)
newAddress.ss_family = AF_UNSPEC;
if (address->domain->address_module->equal_addresses(
(sockaddr*)&oldAddress, (sockaddr*)&newAddress)) {
TRACE(" option %" B_PRId32 " addresses are equal!\n", option);
return B_OK;
}
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;
}
static Interface*
find_interface(const char* name)
{
ASSERT_LOCKED_RECURSIVE(&sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (!strcmp(interface->name, name))
return interface;
}
return NULL;
}
static Interface*
find_interface(uint32 index)
{
ASSERT_LOCKED_RECURSIVE(&sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (interface->index == index)
return interface;
}
return NULL;
}
status_t
add_interface(const char* name, net_domain_private* domain,
const ifaliasreq& request, net_device_interface* deviceInterface)
{
RecursiveLocker 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();
locker.Unlock();
status_t status = add_interface_address(interface, domain, request);
if (status == B_OK)
notify_interface_added(interface);
else {
locker.Lock();
sInterfaces.Remove(interface);
locker.Unlock();
interface->ReleaseReference();
}
interface->ReleaseReference();
return status;
}
void
remove_interface(Interface* interface)
{
interface->SetDown();
interface->RemoveAddresses();
RecursiveLocker locker(sLock);
sInterfaces.Remove(interface);
locker.Unlock();
notify_interface_removed(interface);
interface->ReleaseReference();
}
void
interface_removed_device_interface(net_device_interface* deviceInterface)
{
RecursiveLocker 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)
{
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) {
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);
sockaddr** _address = interfaceAddress->AddressFor(option);
if (_address == NULL)
return B_BAD_VALUE;
Interface* interface = (Interface*)interfaceAddress->interface;
interfaceAddress->RemoveDefaultRoutes(option);
if (option == SIOCDIFADDR) {
locker.Unlock();
invalidate_routes(interfaceAddress);
interface->RemoveAddress(interfaceAddress);
interfaceAddress->ReleaseReference();
return B_OK;
}
if (interfaceAddress->LocalIsDefined())
sAddressTable.Remove(interfaceAddress);
status_t status = InterfaceAddress::Set(_address, newAddress);
if (status == B_OK) {
sockaddr* address = *_address;
if (option == SIOCSIFADDR || option == SIOCSIFNETMASK) {
net_domain* domain = interfaceAddress->domain;
sockaddr* defaultNetmask = NULL;
const sockaddr* netmask = NULL;
if (option == SIOCSIFADDR) {
defaultNetmask = InterfaceAddress::Prepare(
&interfaceAddress->mask, address->sa_len);
} else
netmask = newAddress;
sockaddr* defaultBroadcast = NULL;
if ((domain->address_module->flags
& NET_ADDRESS_MODULE_FLAG_BROADCAST_ADDRESS) != 0) {
defaultBroadcast = InterfaceAddress::Prepare(
&interfaceAddress->destination, address->sa_len);
} else
InterfaceAddress::Set(&interfaceAddress->destination, NULL);
domain->address_module->set_to_defaults(defaultNetmask,
defaultBroadcast, interfaceAddress->local, netmask);
}
interfaceAddress->AddDefaultRoutes(option);
notify_interface_changed(interface);
}
if (interfaceAddress->LocalIsDefined())
sAddressTable.Insert(interfaceAddress);
return status;
}
Interface*
get_interface(net_domain* domain, uint32 index)
{
RecursiveLocker locker(sLock);
Interface* interface;
if (index == 0)
interface = sInterfaces.First();
else
interface = find_interface(index);
if (interface == NULL || interface->IsBusy())
return NULL;
BReference<Interface> interfaceRef(interface);
locker.Unlock();
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
return NULL;
return interfaceRef.Detach();
}
Interface*
get_interface(net_domain* domain, const char* name)
{
RecursiveLocker locker(sLock);
Interface* interface = find_interface(name);
if (interface == NULL || interface->IsBusy())
return NULL;
BReference<Interface> interfaceRef(interface);
locker.Unlock();
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
return NULL;
return interfaceRef.Detach();
}
Interface*
get_interface_for_device(net_domain* domain, uint32 index)
{
RecursiveLocker locker(sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (interface->device->index == index) {
if (interface->IsBusy())
return NULL;
BReference<Interface> interfaceRef(interface);
locker.Unlock();
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
return NULL;
return interfaceRef.Detach();
}
}
return NULL;
}
Interface*
get_interface_for_link(net_domain* domain, const sockaddr* _linkAddress)
{
sockaddr_dl& linkAddress = *(sockaddr_dl*)_linkAddress;
RecursiveLocker locker(sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (interface->IsBusy())
continue;
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->IsBusy())
return NULL;
BReference<Interface> interfaceRef(interface);
locker.Unlock();
if (interface->CreateDomainDatalinkIfNeeded(domain) != B_OK)
return NULL;
return interfaceRef.Detach();
}
}
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)
{
RecursiveLocker locker(sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (interface->IsBusy())
continue;
InterfaceAddress* address
= interface->AddressForDestination(domain, destination);
if (address != NULL)
return address;
}
return NULL;
}
InterfaceAddress*
get_interface_address_for_link(net_domain* domain, const sockaddr* address,
bool unconfiguredOnly)
{
sockaddr_dl& linkAddress = *(sockaddr_dl*)address;
RecursiveLocker locker(sLock);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
if (interface->IsBusy())
continue;
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);
if (unconfiguredOnly)
return interface->FirstUnconfiguredForFamily(domain->family);
return interface->FirstForFamily(domain->family);
}
}
return NULL;
}
uint32
count_interfaces()
{
RecursiveLocker locker(sLock);
return sInterfaces.Count();
}
status_t
list_interfaces(int family, void* _buffer, size_t* bufferSize)
{
RecursiveLocker locker(sLock);
UserBuffer buffer(_buffer, *bufferSize);
InterfaceList::Iterator iterator = sInterfaces.GetIterator();
while (Interface* interface = iterator.Next()) {
buffer.Push(interface->name, IF_NAMESIZE);
InterfaceAddress* address = interface->FirstForFamily(family);
size_t length = 0;
if (address != NULL && address->local != NULL) {
buffer.Push(address->local, length = address->local->sa_len);
} else {
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)) {
buffer.Pad(sizeof(ifreq) - IF_NAMESIZE - length);
}
if (buffer.Status() != B_OK)
return buffer.Status();
}
*bufferSize = buffer.BytesConsumed();
return B_OK;
}
status_t
init_interfaces()
{
recursive_lock_init(&sLock, "net interfaces");
mutex_init(&sHashLock, "net local addresses");
new (&sInterfaces) InterfaceList;
new (&sAddressTable) AddressTable;
#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);
remove_debugger_command("net_interfaces", &dump_interfaces);
remove_debugger_command("net_local", &dump_local);
remove_debugger_command("net_route", &dump_route);
#endif
recursive_lock_destroy(&sLock);
mutex_destroy(&sHashLock);
return B_OK;
}