michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* michael@0: * Copyright (C) 2009 The Android Open Source Project michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include "ifaddrs-android-ext.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "ScopedFd.h" michael@0: #include "LocalArray.h" michael@0: michael@0: // Returns a pointer to the first byte in the address data (which is michael@0: // stored in network byte order). michael@0: uint8_t* sockaddrBytes(int family, sockaddr_storage* ss) { michael@0: if (family == AF_INET) { michael@0: sockaddr_in* ss4 = reinterpret_cast(ss); michael@0: return reinterpret_cast(&ss4->sin_addr); michael@0: } else if (family == AF_INET6) { michael@0: sockaddr_in6* ss6 = reinterpret_cast(ss); michael@0: return reinterpret_cast(&ss6->sin6_addr); michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: // Sadly, we can't keep the interface index for portability with BSD. michael@0: // We'll have to keep the name instead, and re-query the index when michael@0: // we need it later. michael@0: bool ifa_setNameAndFlagsByIndex(ifaddrs *self, int interfaceIndex) { michael@0: // Get the name. michael@0: char buf[IFNAMSIZ]; michael@0: char* name = if_indextoname(interfaceIndex, buf); michael@0: if (name == NULL) { michael@0: return false; michael@0: } michael@0: self->ifa_name = new char[strlen(name) + 1]; michael@0: strcpy(self->ifa_name, name); michael@0: michael@0: // Get the flags. michael@0: ScopedFd fd(socket(AF_INET, SOCK_DGRAM, 0)); michael@0: if (fd.get() == -1) { michael@0: return false; michael@0: } michael@0: ifreq ifr; michael@0: memset(&ifr, 0, sizeof(ifr)); michael@0: strcpy(ifr.ifr_name, name); michael@0: int rc = ioctl(fd.get(), SIOCGIFFLAGS, &ifr); michael@0: if (rc == -1) { michael@0: return false; michael@0: } michael@0: self->ifa_flags = ifr.ifr_flags; michael@0: return true; michael@0: } michael@0: michael@0: // Netlink gives us the address family in the header, and the michael@0: // sockaddr_in or sockaddr_in6 bytes as the payload. We need to michael@0: // stitch the two bits together into the sockaddr that's part of michael@0: // our portable interface. michael@0: void ifa_setAddress(ifaddrs *self, int family, void* data, size_t byteCount) { michael@0: // Set the address proper... michael@0: sockaddr_storage* ss = new sockaddr_storage; michael@0: memset(ss, 0, sizeof(*ss)); michael@0: self->ifa_addr = reinterpret_cast(ss); michael@0: ss->ss_family = family; michael@0: uint8_t* dst = sockaddrBytes(family, ss); michael@0: memcpy(dst, data, byteCount); michael@0: } michael@0: michael@0: // Netlink gives us the prefix length as a bit count. We need to turn michael@0: // that into a BSD-compatible netmask represented by a sockaddr*. michael@0: void ifa_setNetmask(ifaddrs *self, int family, size_t prefixLength) { michael@0: // ...and work out the netmask from the prefix length. michael@0: sockaddr_storage* ss = new sockaddr_storage; michael@0: memset(ss, 0, sizeof(*ss)); michael@0: self->ifa_netmask = reinterpret_cast(ss); michael@0: ss->ss_family = family; michael@0: uint8_t* dst = sockaddrBytes(family, ss); michael@0: memset(dst, 0xff, prefixLength / 8); michael@0: if ((prefixLength % 8) != 0) { michael@0: dst[prefixLength/8] = (0xff << (8 - (prefixLength % 8))); michael@0: } michael@0: } michael@0: michael@0: // FIXME: use iovec instead. michael@0: struct addrReq_struct { michael@0: nlmsghdr netlinkHeader; michael@0: ifaddrmsg msg; michael@0: }; michael@0: michael@0: inline bool sendNetlinkMessage(int fd, const void* data, size_t byteCount) { michael@0: ssize_t sentByteCount = TEMP_FAILURE_RETRY(send(fd, data, byteCount, 0)); michael@0: return (sentByteCount == static_cast(byteCount)); michael@0: } michael@0: michael@0: inline ssize_t recvNetlinkMessage(int fd, char* buf, size_t byteCount) { michael@0: return TEMP_FAILURE_RETRY(recv(fd, buf, byteCount, 0)); michael@0: } michael@0: michael@0: // Source-compatible with the BSD function. michael@0: int getifaddrs(ifaddrs** result) michael@0: { michael@0: // Simplify cleanup for callers. michael@0: *result = NULL; michael@0: michael@0: // Create a netlink socket. michael@0: ScopedFd fd(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)); michael@0: if (fd.get() < 0) { michael@0: return -1; michael@0: } michael@0: michael@0: // Ask for the address information. michael@0: addrReq_struct addrRequest; michael@0: memset(&addrRequest, 0, sizeof(addrRequest)); michael@0: addrRequest.netlinkHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_MATCH; michael@0: addrRequest.netlinkHeader.nlmsg_type = RTM_GETADDR; michael@0: addrRequest.netlinkHeader.nlmsg_len = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(addrRequest))); michael@0: addrRequest.msg.ifa_family = AF_UNSPEC; // All families. michael@0: addrRequest.msg.ifa_index = 0; // All interfaces. michael@0: if (!sendNetlinkMessage(fd.get(), &addrRequest, addrRequest.netlinkHeader.nlmsg_len)) { michael@0: return -1; michael@0: } michael@0: michael@0: // Read the responses. michael@0: LocalArray<0> buf(65536); // We don't necessarily have std::vector. michael@0: ssize_t bytesRead; michael@0: while ((bytesRead = recvNetlinkMessage(fd.get(), &buf[0], buf.size())) > 0) { michael@0: nlmsghdr* hdr = reinterpret_cast(&buf[0]); michael@0: for (; NLMSG_OK(hdr, (size_t)bytesRead); hdr = NLMSG_NEXT(hdr, bytesRead)) { michael@0: switch (hdr->nlmsg_type) { michael@0: case NLMSG_DONE: michael@0: return 0; michael@0: case NLMSG_ERROR: michael@0: return -1; michael@0: case RTM_NEWADDR: michael@0: { michael@0: ifaddrmsg* address = reinterpret_cast(NLMSG_DATA(hdr)); michael@0: rtattr* rta = IFA_RTA(address); michael@0: size_t ifaPayloadLength = IFA_PAYLOAD(hdr); michael@0: while (RTA_OK(rta, ifaPayloadLength)) { michael@0: if (rta->rta_type == IFA_LOCAL) { michael@0: int family = address->ifa_family; michael@0: if (family == AF_INET || family == AF_INET6) { michael@0: ifaddrs *next = *result; michael@0: *result = new ifaddrs; michael@0: memset(*result, 0, sizeof(ifaddrs)); michael@0: (*result)->ifa_next = next; michael@0: if (!ifa_setNameAndFlagsByIndex(*result, address->ifa_index)) { michael@0: return -1; michael@0: } michael@0: ifa_setAddress(*result, family, RTA_DATA(rta), RTA_PAYLOAD(rta)); michael@0: ifa_setNetmask(*result, family, address->ifa_prefixlen); michael@0: } michael@0: } michael@0: rta = RTA_NEXT(rta, ifaPayloadLength); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: // We only get here if recv fails before we see a NLMSG_DONE. michael@0: return -1; michael@0: } michael@0: michael@0: // Source-compatible with the BSD function. michael@0: void freeifaddrs(ifaddrs* addresses) { michael@0: ifaddrs* self = addresses; michael@0: while (self != NULL) { michael@0: delete[] self->ifa_name; michael@0: delete self->ifa_addr; michael@0: delete self->ifa_netmask; michael@0: ifaddrs* next = self->ifa_next; michael@0: delete self; michael@0: self = next; michael@0: } michael@0: }