* Copyright 2010, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*/
#include <NetworkInterface.h>
#include <errno.h>
#include <net/if.h>
#include <sys/sockio.h>
#include <AutoDeleter.h>
#include <Messenger.h>
#include <NetServer.h>
#include <NetworkRoute.h>
static int
family_from_interface_address(const BNetworkInterfaceAddress& address)
{
if (address.Address().Family() != AF_UNSPEC)
return address.Address().Family();
if (address.Mask().Family() != AF_UNSPEC)
return address.Mask().Family();
if (address.Destination().Family() != AF_UNSPEC)
return address.Destination().Family();
return AF_INET;
}
static status_t
do_ifaliasreq(const char* name, int32 option, BNetworkInterfaceAddress& address,
bool readBack = false)
{
int family = AF_INET;
if (!readBack)
family = family_from_interface_address(address);
FileDescriptorCloser socket(::socket(family, SOCK_DGRAM, 0));
if (!socket.IsSet())
return errno;
ifaliasreq request;
strlcpy(request.ifra_name, name, IF_NAMESIZE);
request.ifra_index = address.Index();
request.ifra_flags = address.Flags();
memcpy(&request.ifra_addr, &address.Address().SockAddr(),
address.Address().Length());
memcpy(&request.ifra_mask, &address.Mask().SockAddr(),
address.Mask().Length());
memcpy(&request.ifra_broadaddr, &address.Broadcast().SockAddr(),
address.Broadcast().Length());
if (ioctl(socket.Get(), option, &request, sizeof(struct ifaliasreq)) < 0)
return errno;
if (readBack) {
address.SetFlags(request.ifra_flags);
address.Address().SetTo(request.ifra_addr);
address.Mask().SetTo(request.ifra_mask);
address.Broadcast().SetTo(request.ifra_broadaddr);
}
return B_OK;
}
static status_t
do_ifaliasreq(const char* name, int32 option,
const BNetworkInterfaceAddress& address)
{
return do_ifaliasreq(name, option,
const_cast<BNetworkInterfaceAddress&>(address));
}
template<typename T> status_t
do_request(int family, T& request, const char* name, int option)
{
FileDescriptorCloser socket(::socket(family, SOCK_DGRAM, 0));
if (!socket.IsSet())
return errno;
strlcpy(((struct ifreq&)request).ifr_name, name, IF_NAMESIZE);
if (ioctl(socket.Get(), option, &request, sizeof(T)) < 0)
return errno;
return B_OK;
}
BNetworkInterfaceAddress::BNetworkInterfaceAddress()
:
fIndex(-1),
fFlags(0)
{
}
BNetworkInterfaceAddress::~BNetworkInterfaceAddress()
{
}
status_t
BNetworkInterfaceAddress::SetTo(const BNetworkInterface& interface, int32 index)
{
fIndex = index;
return do_ifaliasreq(interface.Name(), B_SOCKET_GET_ALIAS, *this, true);
}
void
BNetworkInterfaceAddress::SetAddress(const BNetworkAddress& address)
{
fAddress = address;
}
void
BNetworkInterfaceAddress::SetMask(const BNetworkAddress& mask)
{
fMask = mask;
}
void
BNetworkInterfaceAddress::SetBroadcast(const BNetworkAddress& broadcast)
{
fBroadcast = broadcast;
}
void
BNetworkInterfaceAddress::SetDestination(const BNetworkAddress& destination)
{
fBroadcast = destination;
}
void
BNetworkInterfaceAddress::SetFlags(uint32 flags)
{
fFlags = flags;
}
BNetworkInterface::BNetworkInterface()
{
Unset();
}
BNetworkInterface::BNetworkInterface(const char* name)
{
SetTo(name);
}
BNetworkInterface::BNetworkInterface(uint32 index)
{
SetTo(index);
}
BNetworkInterface::~BNetworkInterface()
{
}
void
BNetworkInterface::Unset()
{
fName[0] = '\0';
}
void
BNetworkInterface::SetTo(const char* name)
{
strlcpy(fName, name, IF_NAMESIZE);
}
status_t
BNetworkInterface::SetTo(uint32 index)
{
ifreq request;
request.ifr_index = index;
status_t status = do_request(AF_INET, request, "", SIOCGIFNAME);
if (status != B_OK)
return status;
strlcpy(fName, request.ifr_name, IF_NAMESIZE);
return B_OK;
}
bool
BNetworkInterface::Exists() const
{
ifreq request;
return do_request(AF_INET, request, Name(), SIOCGIFINDEX) == B_OK;
}
const char*
BNetworkInterface::Name() const
{
return fName;
}
uint32
BNetworkInterface::Index() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFINDEX) != B_OK)
return 0;
return request.ifr_index;
}
uint32
BNetworkInterface::Flags() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFFLAGS) != B_OK)
return 0;
return request.ifr_flags;
}
uint32
BNetworkInterface::MTU() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFMTU) != B_OK)
return 0;
return request.ifr_mtu;
}
int32
BNetworkInterface::Media() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFMEDIA) != B_OK)
return -1;
return request.ifr_media;
}
uint32
BNetworkInterface::Metric() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFMETRIC) != B_OK)
return 0;
return request.ifr_metric;
}
uint32
BNetworkInterface::Type() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), SIOCGIFTYPE) != B_OK)
return 0;
return request.ifr_type;
}
status_t
BNetworkInterface::GetStats(ifreq_stats& stats)
{
ifreq request;
status_t status = do_request(AF_INET, request, Name(), SIOCGIFSTATS);
if (status != B_OK)
return status;
memcpy(&stats, &request.ifr_stats, sizeof(ifreq_stats));
return B_OK;
}
bool
BNetworkInterface::HasLink() const
{
return (Flags() & IFF_LINK) != 0;
}
status_t
BNetworkInterface::SetFlags(uint32 flags)
{
ifreq request;
request.ifr_flags = flags;
return do_request(AF_INET, request, Name(), SIOCSIFFLAGS);
}
status_t
BNetworkInterface::SetMTU(uint32 mtu)
{
ifreq request;
request.ifr_mtu = mtu;
return do_request(AF_INET, request, Name(), SIOCSIFMTU);
}
status_t
BNetworkInterface::SetMedia(int32 media)
{
ifreq request;
request.ifr_media = media;
return do_request(AF_INET, request, Name(), SIOCSIFMEDIA);
}
status_t
BNetworkInterface::SetMetric(uint32 metric)
{
ifreq request;
request.ifr_metric = metric;
return do_request(AF_INET, request, Name(), SIOCSIFMETRIC);
}
int32
BNetworkInterface::CountAddresses() const
{
ifreq request;
if (do_request(AF_INET, request, Name(), B_SOCKET_COUNT_ALIASES) != B_OK)
return 0;
return request.ifr_count;
}
status_t
BNetworkInterface::GetAddressAt(int32 index, BNetworkInterfaceAddress& address)
{
return address.SetTo(*this, index);
}
int32
BNetworkInterface::FindAddress(const BNetworkAddress& address)
{
FileDescriptorCloser socket(::socket(address.Family(), SOCK_DGRAM, 0));
if (!socket.IsSet())
return -1;
ifaliasreq request;
memset(&request, 0, sizeof(ifaliasreq));
strlcpy(request.ifra_name, Name(), IF_NAMESIZE);
request.ifra_index = -1;
memcpy(&request.ifra_addr, &address.SockAddr(), address.Length());
if (ioctl(socket.Get(), B_SOCKET_GET_ALIAS, &request,
sizeof(struct ifaliasreq)) < 0) {
return -1;
}
return request.ifra_index;
}
int32
BNetworkInterface::FindFirstAddress(int family)
{
FileDescriptorCloser socket(::socket(family, SOCK_DGRAM, 0));
if (!socket.IsSet())
return -1;
ifaliasreq request;
memset(&request, 0, sizeof(ifaliasreq));
strlcpy(request.ifra_name, Name(), IF_NAMESIZE);
request.ifra_index = -1;
request.ifra_addr.ss_family = AF_UNSPEC;
if (ioctl(socket.Get(), B_SOCKET_GET_ALIAS, &request,
sizeof(struct ifaliasreq)) < 0) {
return -1;
}
return request.ifra_index;
}
status_t
BNetworkInterface::AddAddress(const BNetworkInterfaceAddress& address)
{
return do_ifaliasreq(Name(), B_SOCKET_ADD_ALIAS, address);
}
status_t
BNetworkInterface::AddAddress(const BNetworkAddress& local)
{
BNetworkInterfaceAddress address;
address.SetAddress(local);
return do_ifaliasreq(Name(), B_SOCKET_ADD_ALIAS, address);
}
status_t
BNetworkInterface::SetAddress(const BNetworkInterfaceAddress& address)
{
return do_ifaliasreq(Name(), B_SOCKET_SET_ALIAS, address);
}
status_t
BNetworkInterface::RemoveAddress(const BNetworkInterfaceAddress& address)
{
ifreq request;
memcpy(&request.ifr_addr, &address.Address().SockAddr(),
address.Address().Length());
return do_request(family_from_interface_address(address), request, Name(),
B_SOCKET_REMOVE_ALIAS);
}
status_t
BNetworkInterface::RemoveAddress(const BNetworkAddress& address)
{
ifreq request;
memcpy(&request.ifr_addr, &address.SockAddr(), address.Length());
return do_request(address.Family(), request, Name(), B_SOCKET_REMOVE_ALIAS);
}
status_t
BNetworkInterface::RemoveAddressAt(int32 index)
{
BNetworkInterfaceAddress address;
status_t status = GetAddressAt(index, address);
if (status != B_OK)
return status;
return RemoveAddress(address);
}
status_t
BNetworkInterface::GetHardwareAddress(BNetworkAddress& address)
{
FileDescriptorCloser socket(::socket(AF_LINK, SOCK_DGRAM, 0));
if (!socket.IsSet())
return errno;
ifreq request;
strlcpy(request.ifr_name, Name(), IF_NAMESIZE);
if (ioctl(socket.Get(), SIOCGIFADDR, &request, sizeof(struct ifreq)) < 0)
return errno;
address.SetTo(request.ifr_addr);
return B_OK;
}
status_t
BNetworkInterface::AddRoute(const BNetworkRoute& route)
{
int family = route.AddressFamily();
if (family == AF_UNSPEC)
return B_BAD_VALUE;
ifreq request;
request.ifr_route = route.RouteEntry();
return do_request(family, request, Name(), SIOCADDRT);
}
status_t
BNetworkInterface::AddDefaultRoute(const BNetworkAddress& gateway)
{
BNetworkRoute route;
status_t result = route.SetGateway(gateway);
if (result != B_OK)
return result;
route.SetFlags(RTF_STATIC | RTF_DEFAULT | RTF_GATEWAY);
return AddRoute(route);
}
status_t
BNetworkInterface::RemoveRoute(const BNetworkRoute& route)
{
int family = route.AddressFamily();
if (family == AF_UNSPEC)
return B_BAD_VALUE;
return RemoveRoute(family, route);
}
status_t
BNetworkInterface::RemoveRoute(int family, const BNetworkRoute& route)
{
ifreq request;
request.ifr_route = route.RouteEntry();
return do_request(family, request, Name(), SIOCDELRT);
}
status_t
BNetworkInterface::RemoveDefaultRoute(int family)
{
BNetworkRoute route;
route.SetFlags(RTF_STATIC | RTF_DEFAULT);
return RemoveRoute(family, route);
}
status_t
BNetworkInterface::GetRoutes(int family,
BObjectList<BNetworkRoute, true>& routes) const
{
return BNetworkRoute::GetRoutes(family, Name(), routes);
}
status_t
BNetworkInterface::GetDefaultRoute(int family, BNetworkRoute& route) const
{
return BNetworkRoute::GetDefaultRoute(family, Name(), route);
}
status_t
BNetworkInterface::GetDefaultGateway(int family, BNetworkAddress& gateway) const
{
return BNetworkRoute::GetDefaultGateway(family, Name(), gateway);
}
status_t
BNetworkInterface::AutoConfigure(int family)
{
BMessage message(kMsgConfigureInterface);
message.AddString("device", Name());
BMessage address;
address.AddInt32("family", family);
address.AddBool("auto_config", true);
message.AddMessage("address", &address);
BMessenger networkServer(kNetServerSignature);
BMessage reply;
status_t status = networkServer.SendMessage(&message, &reply);
if (status == B_OK)
reply.FindInt32("status", &status);
return status;
}