/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "common.h" #include "ifreqs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android::netdevice { void useSocketDomain(int domain) { ifreqs::socketDomain = domain; } bool exists(std::string_view ifname) { return nametoindex(ifname) != 0; } bool up(std::string_view ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false; if (ifr.ifr_flags & IFF_UP) return true; ifr.ifr_flags |= IFF_UP; return ifreqs::send(SIOCSIFFLAGS, ifr); } bool down(std::string_view ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return false; if (!(ifr.ifr_flags & IFF_UP)) return true; ifr.ifr_flags &= ~IFF_UP; return ifreqs::send(SIOCSIFFLAGS, ifr); } static std::string toString(const sockaddr* addr) { char host[NI_MAXHOST]; socklen_t addrlen = (addr->sa_family == AF_INET) ? sizeof(sockaddr_in) : sizeof(sockaddr_in6); auto res = getnameinfo(addr, addrlen, host, sizeof(host), nullptr, 0, NI_NUMERICHOST); CHECK(res == 0) << "getnameinfo failed: " << gai_strerror(res); return host; } static std::unique_ptr getifaddrs() { ifaddrs* addrs = nullptr; CHECK(getifaddrs(&addrs) == 0) << "getifaddrs failed: " << strerror(errno); return {addrs, freeifaddrs}; } std::set getAllAddr4(std::string_view ifname) { std::set addresses; auto addrs = getifaddrs(); for (ifaddrs* addr = addrs.get(); addr != nullptr; addr = addr->ifa_next) { if (ifname != addr->ifa_name) continue; if (addr->ifa_addr == nullptr) continue; if (addr->ifa_addr->sa_family != AF_INET) continue; addresses.insert(toString(addr->ifa_addr)); } return addresses; } static in_addr_t inetAddr(std::string_view addr) { auto addrn = inet_addr(std::string(addr).c_str()); CHECK(addrn != INADDR_NONE) << "Invalid address " << addr; return addrn; } static in_addr_t prefixLengthToIpv4Netmask(uint8_t prefixlen) { in_addr_t zero = 0; return htonl(~zero << (32 - prefixlen)); } bool setAddr4(std::string_view ifname, std::string_view addr, std::optional prefixlen) { auto ifr = ifreqs::fromName(ifname); auto ifrAddr = reinterpret_cast(&ifr.ifr_addr); ifrAddr->sin_family = AF_INET; ifrAddr->sin_addr.s_addr = inetAddr(addr); if (!ifreqs::send(SIOCSIFADDR, ifr)) return false; if (prefixlen.has_value()) { if (*prefixlen < 0 || *prefixlen > 32) { LOG(ERROR) << "Invalid prefix length: " << *prefixlen; return false; } ifr = ifreqs::fromName(ifname); auto ifrNetmask = reinterpret_cast(&ifr.ifr_netmask); ifrNetmask->sin_family = AF_INET; ifrNetmask->sin_addr.s_addr = prefixLengthToIpv4Netmask(*prefixlen); if (!ifreqs::send(SIOCSIFNETMASK, ifr)) return false; } return true; } bool addAddr4(std::string_view ifname, std::string_view addr, uint8_t prefixlen) { nl::MessageFactory req(RTM_NEWADDR, nl::kCreateFlags); req->ifa_family = AF_INET; req->ifa_prefixlen = prefixlen; req->ifa_flags = IFA_F_SECONDARY; req->ifa_index = nametoindex(ifname); auto addrn = inetAddr(addr); req.add(IFLA_ADDRESS, addrn); req.add(IFLA_BROADCAST, addrn); nl::Socket sock(NETLINK_ROUTE); return sock.send(req) && sock.receiveAck(req); } bool add(std::string_view dev, std::string_view type) { nl::MessageFactory req(RTM_NEWLINK, nl::kCreateFlags); req.add(IFLA_IFNAME, dev); { auto linkinfo = req.addNested(IFLA_LINKINFO); req.addBuffer(IFLA_INFO_KIND, type); } nl::Socket sock(NETLINK_ROUTE); return sock.send(req) && sock.receiveAck(req); } bool del(std::string_view dev) { nl::MessageFactory req(RTM_DELLINK); req.add(IFLA_IFNAME, dev); nl::Socket sock(NETLINK_ROUTE); return sock.send(req) && sock.receiveAck(req); } std::optional getHwAddr(std::string_view ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFHWADDR, ifr)) return std::nullopt; hwaddr_t hwaddr; memcpy(hwaddr.data(), ifr.ifr_hwaddr.sa_data, hwaddr.size()); return hwaddr; } bool setHwAddr(std::string_view ifname, hwaddr_t hwaddr) { auto ifr = ifreqs::fromName(ifname); // fetch sa_family if (!ifreqs::send(SIOCGIFHWADDR, ifr)) return false; memcpy(ifr.ifr_hwaddr.sa_data, hwaddr.data(), hwaddr.size()); return ifreqs::send(SIOCSIFHWADDR, ifr); } std::optional isUp(std::string_view ifname) { auto ifr = ifreqs::fromName(ifname); if (!ifreqs::send(SIOCGIFFLAGS, ifr)) return std::nullopt; return ifr.ifr_flags & IFF_UP; } static bool hasIpv4(std::string_view ifname) { auto ifr = ifreqs::fromName(ifname); switch (ifreqs::trySend(SIOCGIFADDR, ifr)) { case 0: return true; case EADDRNOTAVAIL: case ENODEV: return false; default: PLOG(WARNING) << "Failed checking IPv4 address"; return false; } } struct WaitState { bool present; bool up; bool hasIpv4Addr; bool satisfied(WaitCondition cnd) const { switch (cnd) { case WaitCondition::PRESENT: return present; case WaitCondition::PRESENT_AND_UP: return present && up; case WaitCondition::PRESENT_AND_IPV4: return present && up && hasIpv4Addr; case WaitCondition::DOWN_OR_GONE: return !present || !up; } } }; static std::string toString(WaitCondition cnd) { switch (cnd) { case WaitCondition::PRESENT: return "become present"; case WaitCondition::PRESENT_AND_UP: return "come up"; case WaitCondition::PRESENT_AND_IPV4: return "get IPv4 address"; case WaitCondition::DOWN_OR_GONE: return "go down"; } } static std::string toString(Quantifier quant) { switch (quant) { case Quantifier::ALL_OF: return "all of"; case Quantifier::ANY_OF: return "any of"; } } static std::string toString(const std::set& ifnames) { std::stringstream ss; std::copy(ifnames.begin(), ifnames.end(), std::ostream_iterator(ss, ",")); auto str = ss.str(); str.pop_back(); return str; } std::optional waitFor(std::set ifnames, WaitCondition cnd, Quantifier quant) { nl::Socket sock(NETLINK_ROUTE, 0, RTMGRP_LINK | RTMGRP_IPV4_IFADDR); using StatesMap = std::map; StatesMap states = {}; for (const auto ifname : ifnames) { const auto present = exists(ifname); const auto up = present && isUp(ifname).value_or(false); const auto hasIpv4Addr = present && hasIpv4(ifname); states[ifname] = {present, up, hasIpv4Addr}; } const auto mapConditionChecker = [cnd](const StatesMap::iterator::value_type& it) { return it.second.satisfied(cnd); }; const auto isFullySatisfied = [&states, quant, mapConditionChecker]() -> std::optional { if (quant == Quantifier::ALL_OF) { if (!std::all_of(states.begin(), states.end(), mapConditionChecker)) return {}; return states.begin()->first; } else { // Quantifier::ANY_OF const auto it = std::find_if(states.begin(), states.end(), mapConditionChecker); if (it == states.end()) return {}; return it->first; } }; if (const auto iface = isFullySatisfied()) return iface; LOG(DEBUG) << "Waiting for " << toString(quant) << " " << toString(ifnames) << " to " << toString(cnd); for (const auto rawMsg : sock) { if (const auto msg = nl::Message::parse(rawMsg, {RTM_NEWLINK, RTM_DELLINK}); msg.has_value()) { // Interface added / removed const auto ifname = msg->attributes.get(IFLA_IFNAME); if (ifnames.count(ifname) == 0) continue; auto& state = states[ifname]; state.present = (msg->header.nlmsg_type != RTM_DELLINK); state.up = state.present && (msg->data.ifi_flags & IFF_UP) != 0; if (!state.present) state.hasIpv4Addr = false; } else if (const auto msg = nl::Message::parse(rawMsg, {RTM_NEWADDR, RTM_DELADDR}); msg.has_value()) { // Address added / removed const auto ifname = msg->attributes.get(IFLA_IFNAME); if (ifnames.count(ifname) == 0) continue; if (msg->header.nlmsg_type == RTM_NEWADDR) { states[ifname].hasIpv4Addr = true; } else { // instead of tracking which one got deleted, let's just ask states[ifname].hasIpv4Addr = hasIpv4(ifname); } } if (const auto iface = isFullySatisfied()) { LOG(DEBUG) << "Finished waiting for " << toString(quant) << " " << toString(ifnames) << " to " << toString(cnd); return iface; } } LOG(FATAL) << "Can't read Netlink socket"; return {}; } } // namespace android::netdevice bool operator==(const android::netdevice::hwaddr_t lhs, const unsigned char rhs[ETH_ALEN]) { static_assert(lhs.size() == ETH_ALEN); return 0 == memcmp(lhs.data(), rhs, lhs.size()); }