libnetdevice: implement ipv4 get/set/add operations

Bug: 372814636
Test: verified with b/372814636 test service
Change-Id: I59af9750f4ba46085f0c7a2a5681243beb68cf39
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
index 4c5be04..75655d5 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/libnetdevice.h
@@ -112,6 +112,37 @@
 bool down(std::string_view ifname);
 
 /**
+ * Retrieves all IPv4 addresses of a given interface.
+ *
+ * \param ifname Interface to query
+ * \return list of IPv4 addresses of this interface
+ */
+std::set<std::string> getAllAddr4(std::string_view ifname);
+
+/**
+ * Set IPv4 address on a given interface.
+ *
+ * This function will overwrite any other existing IPv4 addresses.
+ *
+ * \param ifname Interface to modify
+ * \param addr IPv4 address to set
+ * \return true in case of success, false otherwise
+ */
+bool setAddr4(std::string_view ifname, std::string_view addr);
+
+/**
+ * Add new IPv4 address to a given interface.
+ *
+ * Please note this doesn't remove existing IPv4 addresses.
+ *
+ * \param ifname Interface to modify
+ * \param addr IPv4 address to add
+ * \param prefixlen IPv4 netmask length
+ * \return true in case of success, false otherwise
+ */
+bool addAddr4(std::string_view ifname, std::string_view addr, uint8_t prefixlen = 24);
+
+/**
  * Adds virtual link.
  *
  * \param dev the name of the new virtual device
diff --git a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
index d655259..4c4b77b 100644
--- a/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
+++ b/automotive/can/1.0/default/libnetdevice/libnetdevice.cpp
@@ -23,9 +23,12 @@
 #include <libnl++/MessageFactory.h>
 #include <libnl++/Socket.h>
 
+#include <arpa/inet.h>
+#include <ifaddrs.h>
 #include <linux/can.h>
 #include <linux/rtnetlink.h>
 #include <net/if.h>
+#include <netdb.h>
 #include <sys/ioctl.h>
 
 #include <algorithm>
@@ -45,6 +48,7 @@
 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);
 }
@@ -52,10 +56,69 @@
 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<ifaddrs, decltype(&freeifaddrs)> getifaddrs() {
+    ifaddrs* addrs = nullptr;
+    CHECK(getifaddrs(&addrs) == 0) << "getifaddrs failed: " << strerror(errno);
+    return {addrs, freeifaddrs};
+}
+
+std::set<std::string> getAllAddr4(std::string_view ifname) {
+    std::set<std::string> 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;
+}
+
+bool setAddr4(std::string_view ifname, std::string_view addr) {
+    auto ifr = ifreqs::fromName(ifname);
+
+    struct sockaddr_in* ifrAddr = reinterpret_cast<sockaddr_in*>(&ifr.ifr_addr);
+    ifrAddr->sin_family = AF_INET;
+    ifrAddr->sin_addr.s_addr = inetAddr(addr);
+
+    return ifreqs::send(SIOCSIFADDR, ifr);
+}
+
+bool addAddr4(std::string_view ifname, std::string_view addr, uint8_t prefixlen) {
+    android::nl::MessageFactory<ifaddrmsg> req(
+            RTM_NEWADDR, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
+    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<ifinfomsg> req(RTM_NEWLINK,
                                       NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);