status_t control_routes(struct net_interface* _interface, net_domain* domain, int32 option, void* argument, size_t length) { TRACE("control_routes(interface %p, domain %p, option %" B_PRId32 ")\n", _interface, domain, option); Interface* interface = (Interface*)_interface; switch (option) { case SIOCADDRT: case SIOCDELRT: { // add or remove a route if (length != sizeof(struct ifreq)) return B_BAD_VALUE; route_entry entry; if (user_memcpy(&entry, &((ifreq*)argument)->ifr_route, sizeof(route_entry)) != B_OK) return B_BAD_ADDRESS; net_route_private route; status_t status; if ((status = user_copy_address(entry.destination, &route.destination)) != B_OK || (status = user_copy_address(entry.mask, &route.mask)) != B_OK || (status = user_copy_address(entry.gateway, &route.gateway)) != B_OK) return status; InterfaceAddress* address = interface->FirstForFamily(domain->family); route.mtu = entry.mtu; route.flags = entry.flags; route.interface_address = address; if (option == SIOCADDRT) status = add_route(domain, &route); else status = remove_route(domain, &route); if (address != NULL) address->ReleaseReference(); return status; } } return B_BAD_VALUE; }
/*! 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) { RecursiveLocker 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; }
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; }
/*! 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) { 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)) { // 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); } 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) { delete address; return status; } // Note, even if setting the address failed, the empty // address added here will still be added to the interface. 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) { // 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) { delete address; 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; }