/* * Copyright 2013-2015 Haiku, Inc. All rights reserved. * Distributed under the terms of the MIT License. */ #include #include #include #include #include #include BNetworkRoute::BNetworkRoute() { memset(&fRouteEntry, 0, sizeof(route_entry)); } BNetworkRoute::~BNetworkRoute() { UnsetDestination(); UnsetMask(); UnsetGateway(); UnsetSource(); } status_t BNetworkRoute::SetTo(const BNetworkRoute& other) { return SetTo(other.RouteEntry()); } status_t BNetworkRoute::SetTo(const route_entry& routeEntry) { #define SET_ADDRESS(address, setFunction) \ if (routeEntry.address != NULL) { \ result = setFunction(*routeEntry.address); \ if (result != B_OK) \ return result; \ } status_t result; SET_ADDRESS(destination, SetDestination) SET_ADDRESS(mask, SetMask) SET_ADDRESS(gateway, SetGateway) SET_ADDRESS(source, SetSource) SetFlags(routeEntry.flags); SetMTU(routeEntry.mtu); return B_OK; } void BNetworkRoute::Adopt(BNetworkRoute& other) { memcpy(&fRouteEntry, &other.fRouteEntry, sizeof(route_entry)); memset(&other.fRouteEntry, 0, sizeof(route_entry)); } const route_entry& BNetworkRoute::RouteEntry() const { return fRouteEntry; } const sockaddr* BNetworkRoute::Destination() const { return fRouteEntry.destination; } status_t BNetworkRoute::SetDestination(const sockaddr& destination) { return _AllocateAndSetAddress(destination, fRouteEntry.destination); } void BNetworkRoute::UnsetDestination() { _FreeAndUnsetAddress(fRouteEntry.destination); } const sockaddr* BNetworkRoute::Mask() const { return fRouteEntry.mask; } status_t BNetworkRoute::SetMask(const sockaddr& mask) { return _AllocateAndSetAddress(mask, fRouteEntry.mask); } void BNetworkRoute::UnsetMask() { _FreeAndUnsetAddress(fRouteEntry.mask); } const sockaddr* BNetworkRoute::Gateway() const { return fRouteEntry.gateway; } status_t BNetworkRoute::SetGateway(const sockaddr& gateway) { return _AllocateAndSetAddress(gateway, fRouteEntry.gateway); } void BNetworkRoute::UnsetGateway() { _FreeAndUnsetAddress(fRouteEntry.gateway); } const sockaddr* BNetworkRoute::Source() const { return fRouteEntry.source; } status_t BNetworkRoute::SetSource(const sockaddr& source) { return _AllocateAndSetAddress(source, fRouteEntry.source); } void BNetworkRoute::UnsetSource() { _FreeAndUnsetAddress(fRouteEntry.source); } uint32 BNetworkRoute::Flags() const { return fRouteEntry.flags; } void BNetworkRoute::SetFlags(uint32 flags) { fRouteEntry.flags = flags; } uint32 BNetworkRoute::MTU() const { return fRouteEntry.mtu; } void BNetworkRoute::SetMTU(uint32 mtu) { fRouteEntry.mtu = mtu; } int BNetworkRoute::AddressFamily() const { #define RETURN_FAMILY_IF_SET(address) \ if (fRouteEntry.address != NULL \ && fRouteEntry.address->sa_family != AF_UNSPEC) { \ return fRouteEntry.address->sa_family; \ } RETURN_FAMILY_IF_SET(destination) RETURN_FAMILY_IF_SET(mask) RETURN_FAMILY_IF_SET(gateway) RETURN_FAMILY_IF_SET(source) return AF_UNSPEC; } status_t BNetworkRoute::GetDefaultRoute(int family, const char* interfaceName, BNetworkRoute& route) { BObjectList routes(1, true); status_t result = GetRoutes(family, interfaceName, RTF_DEFAULT, routes); if (result != B_OK) return result; if (routes.CountItems() == 0) return B_ENTRY_NOT_FOUND; route.Adopt(*routes.ItemAt(0)); return B_OK; } status_t BNetworkRoute::GetDefaultGateway(int family, const char* interfaceName, sockaddr& gateway) { BNetworkRoute route; status_t result = GetDefaultRoute(family, interfaceName, route); if (result != B_OK) return result; const sockaddr* defaultGateway = route.Gateway(); if (defaultGateway == NULL) return B_ENTRY_NOT_FOUND; memcpy(&gateway, defaultGateway, defaultGateway->sa_len); return B_OK; } status_t BNetworkRoute::GetRoutes(int family, BObjectList& routes) { return GetRoutes(family, NULL, 0, routes); } status_t BNetworkRoute::GetRoutes(int family, const char* interfaceName, BObjectList& routes) { return GetRoutes(family, interfaceName, 0, routes); } status_t BNetworkRoute::GetRoutes(int family, const char* interfaceName, uint32 filterFlags, BObjectList& routes) { FileDescriptorCloser socket(::socket(family, SOCK_DGRAM, 0)); if (!socket.IsSet()) return errno; ifconf config; config.ifc_len = sizeof(config.ifc_value); if (ioctl(socket.Get(), SIOCGRTSIZE, &config, sizeof(struct ifconf)) < 0) return errno; uint32 size = (uint32)config.ifc_value; if (size == 0) return B_OK; void* buffer = malloc(size); if (buffer == NULL) return B_NO_MEMORY; MemoryDeleter bufferDeleter(buffer); config.ifc_len = size; config.ifc_buf = buffer; if (ioctl(socket.Get(), SIOCGRTTABLE, &config, sizeof(struct ifconf)) < 0) return errno; ifreq* interface = (ifreq*)buffer; ifreq* end = (ifreq*)((uint8*)buffer + size); while (interface < end) { route_entry& routeEntry = interface->ifr_route; if ((interfaceName == NULL || strcmp(interface->ifr_name, interfaceName) == 0) && (filterFlags == 0 || (routeEntry.flags & filterFlags) != 0)) { BNetworkRoute* route = new(std::nothrow) BNetworkRoute; if (route == NULL) return B_NO_MEMORY; // Note that source is not provided in the buffer. routeEntry.source = NULL; status_t result = route->SetTo(routeEntry); if (result != B_OK) { delete route; return result; } if (!routes.AddItem(route)) { delete route; return B_NO_MEMORY; } } size_t addressSize = 0; if (routeEntry.destination != NULL) addressSize += routeEntry.destination->sa_len; if (routeEntry.mask != NULL) addressSize += routeEntry.mask->sa_len; if (routeEntry.gateway != NULL) addressSize += routeEntry.gateway->sa_len; interface = (ifreq *)((addr_t)interface + IF_NAMESIZE + sizeof(route_entry) + addressSize); } return B_OK; } status_t BNetworkRoute::_AllocateAndSetAddress(const sockaddr& from, sockaddr*& to) { if (from.sa_len > sizeof(sockaddr_storage)) return B_BAD_VALUE; if (to == NULL) { to = (sockaddr*)malloc(sizeof(sockaddr_storage)); if (to == NULL) return B_NO_MEMORY; } memcpy(to, &from, from.sa_len); return B_OK; } void BNetworkRoute::_FreeAndUnsetAddress(sockaddr*& address) { free(address); address = NULL; }