Merge "Implement if_nameindex(3)/if_freenameindex(3)."
diff --git a/libc/Android.mk b/libc/Android.mk
index bf4be8c..04941f1 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -44,8 +44,6 @@
     bionic/ether_ntoa.c \
     bionic/fts.c \
     bionic/getpriority.c \
-    bionic/if_indextoname.c \
-    bionic/if_nametoindex.c \
     bionic/initgroups.c \
     bionic/isatty.c \
     bionic/memmem.c \
@@ -107,6 +105,7 @@
     bionic/arpa_inet.cpp \
     bionic/assert.cpp \
     bionic/atof.cpp \
+    bionic/bionic_netlink.cpp \
     bionic/bionic_systrace.cpp \
     bionic/bionic_time_conversions.cpp \
     bionic/brk.cpp \
@@ -173,6 +172,7 @@
     bionic/mntent.cpp \
     bionic/mremap.cpp \
     bionic/NetdClientDispatch.cpp \
+    bionic/net_if.cpp \
     bionic/open.cpp \
     bionic/pathconf.cpp \
     bionic/pause.cpp \
diff --git a/libc/bionic/bionic_netlink.cpp b/libc/bionic/bionic_netlink.cpp
new file mode 100644
index 0000000..19ca88d
--- /dev/null
+++ b/libc/bionic/bionic_netlink.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "bionic_netlink.h"
+
+#include <errno.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "private/ErrnoRestorer.h"
+
+NetlinkConnection::NetlinkConnection() {
+  fd_ = -1;
+
+  // The kernel keeps packets under 8KiB (NLMSG_GOODSIZE),
+  // but that's a bit too large to go on the stack.
+  size_ = 8192;
+  data_ = new char[size_];
+}
+
+NetlinkConnection::~NetlinkConnection() {
+  ErrnoRestorer errno_restorer;
+  if (fd_ != -1) close(fd_);
+  delete[] data_;
+}
+
+bool NetlinkConnection::SendRequest(int type) {
+  // Rather than force all callers to check for the unlikely event of being
+  // unable to allocate 8KiB, check here.
+  if (data_ == nullptr) return false;
+
+  // Did we open a netlink socket yet?
+  if (fd_ == -1) {
+    fd_ = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
+  }
+
+  // Construct and send the message.
+  struct NetlinkMessage {
+    nlmsghdr hdr;
+    rtgenmsg msg;
+  } request;
+  memset(&request, 0, sizeof(request));
+  request.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
+  request.hdr.nlmsg_type = type;
+  request.hdr.nlmsg_len = sizeof(request);
+  request.msg.rtgen_family = AF_UNSPEC; // All families.
+  return (TEMP_FAILURE_RETRY(send(fd_, &request, sizeof(request), 0)) == sizeof(request));
+}
+
+bool NetlinkConnection::ReadResponses(void callback(void*, nlmsghdr*), void* context) {
+  // Read through all the responses, handing interesting ones to the callback.
+  ssize_t bytes_read;
+  while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd_, data_, size_, 0))) > 0) {
+    nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(data_);
+    for (; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr = NLMSG_NEXT(hdr, bytes_read)) {
+      if (hdr->nlmsg_type == NLMSG_DONE) return true;
+      if (hdr->nlmsg_type == NLMSG_ERROR) return false;
+      callback(context, hdr);
+    }
+  }
+
+  // We only get here if recv fails before we see a NLMSG_DONE.
+  return false;
+}
diff --git a/libc/bionic/if_nametoindex.c b/libc/bionic/bionic_netlink.h
similarity index 63%
rename from libc/bionic/if_nametoindex.c
rename to libc/bionic/bionic_netlink.h
index d670e43..4b103f3 100644
--- a/libc/bionic/if_nametoindex.c
+++ b/libc/bionic/bionic_netlink.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -26,33 +26,34 @@
  * SUCH DAMAGE.
  */
 
-#include <string.h>
-#include <unistd.h>
-#include <linux/sockios.h>
-#include <net/if.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
+#ifndef BIONIC_NETLINK_H
+#define BIONIC_NETLINK_H
 
-/*
- * Map an interface name into its corresponding index.
- * Returns 0 on error, as 0 is not a valid index.
- */
-unsigned int if_nametoindex(const char *ifname)
-{
-    int index;
-    int ctl_sock;
-    struct ifreq ifr;
+#include <sys/types.h>
 
-    memset(&ifr, 0, sizeof(struct ifreq));
-    strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
-    ifr.ifr_name[IFNAMSIZ - 1] = 0;
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
 
-    index = 0;
-    if ((ctl_sock = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
-        if (ioctl(ctl_sock, SIOCGIFINDEX, &ifr) >= 0) {
-            index = ifr.ifr_ifindex;
-        }
-        close(ctl_sock);
-    }
-    return index;
-}
+struct nlmsghdr;
+
+class NetlinkConnection {
+ public:
+  NetlinkConnection();
+  ~NetlinkConnection();
+
+  bool SendRequest(int type);
+  bool ReadResponses(void callback(void*, nlmsghdr*), void* context);
+
+ private:
+  int fd_;
+  char* data_;
+  size_t size_;
+};
+
+#if !defined(__clang__)
+// GCC gets confused by NLMSG_DATA and doesn't realize that the old-style
+// cast is from a system header and should be ignored.
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#endif
+
+#endif
diff --git a/libc/bionic/if_indextoname.c b/libc/bionic/if_indextoname.c
deleted file mode 100644
index f0db512..0000000
--- a/libc/bionic/if_indextoname.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <string.h>
-#include <unistd.h>
-#include <linux/sockios.h>
-#include <net/if.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-
-/*
- * Map an interface index into its name.
- * Returns NULL on error.
- */
-char*
-if_indextoname(unsigned ifindex, char *ifname)
-{
-    int ctl_sock;
-    struct ifreq ifr;
-    char*  ret = NULL;
-
-    memset(&ifr, 0, sizeof(struct ifreq));
-    ifr.ifr_ifindex = ifindex;
-
-    if ((ctl_sock = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
-        if (ioctl(ctl_sock, SIOCGIFNAME, &ifr) >= 0) {
-            ret = strncpy (ifname, ifr.ifr_name, IFNAMSIZ);
-        } else {
-            /* Posix requires ENXIO */
-            if (errno == ENODEV)
-                errno = ENXIO;
-        }
-        close(ctl_sock);
-    }
-    return ret;
-}
diff --git a/libc/bionic/ifaddrs.cpp b/libc/bionic/ifaddrs.cpp
index b66883e..d03d76f 100644
--- a/libc/bionic/ifaddrs.cpp
+++ b/libc/bionic/ifaddrs.cpp
@@ -30,8 +30,6 @@
 
 #include <errno.h>
 #include <linux/if_packet.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
 #include <net/if.h>
 #include <netinet/in.h>
 #include <stdint.h>
@@ -40,6 +38,10 @@
 #include <string.h>
 #include <unistd.h>
 
+#include "private/ErrnoRestorer.h"
+
+#include "bionic_netlink.h"
+
 // The public ifaddrs struct is full of pointers. Rather than track several
 // different allocations, we use a maximally-sized structure with the public
 // part at offset 0, and pointers into its hidden tail.
@@ -119,13 +121,9 @@
   }
 };
 
-#if !defined(__clang__)
-// GCC gets confused by NLMSG_DATA and doesn't realize that the old-style
-// cast is from a system header and should be ignored.
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-#endif
+static void __getifaddrs_callback(void* context, nlmsghdr* hdr) {
+  ifaddrs** out = reinterpret_cast<ifaddrs**>(context);
 
-static void __handle_netlink_response(ifaddrs** out, nlmsghdr* hdr) {
   if (hdr->nlmsg_type == RTM_NEWLINK) {
     ifinfomsg* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr));
 
@@ -195,62 +193,22 @@
   }
 }
 
-static bool __send_netlink_request(int fd, int type) {
-  struct NetlinkMessage {
-    nlmsghdr hdr;
-    rtgenmsg msg;
-  } request;
-  memset(&request, 0, sizeof(request));
-  request.hdr.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
-  request.hdr.nlmsg_type = type;
-  request.hdr.nlmsg_len = sizeof(request);
-  request.msg.rtgen_family = AF_UNSPEC; // All families.
-  return (TEMP_FAILURE_RETRY(send(fd, &request, sizeof(request), 0)) == sizeof(request));
-}
-
-static bool __read_netlink_responses(int fd, ifaddrs** out, char* buf, size_t buf_len) {
-  ssize_t bytes_read;
-  // Read through all the responses, handing interesting ones to __handle_netlink_response.
-  while ((bytes_read = TEMP_FAILURE_RETRY(recv(fd, buf, buf_len, 0))) > 0) {
-    nlmsghdr* hdr = reinterpret_cast<nlmsghdr*>(buf);
-    for (; NLMSG_OK(hdr, static_cast<size_t>(bytes_read)); hdr = NLMSG_NEXT(hdr, bytes_read)) {
-      if (hdr->nlmsg_type == NLMSG_DONE) return true;
-      if (hdr->nlmsg_type == NLMSG_ERROR) return false;
-      __handle_netlink_response(out, hdr);
-    }
-  }
-  // We only get here if recv fails before we see a NLMSG_DONE.
-  return false;
-}
-
 int getifaddrs(ifaddrs** out) {
-  // Make cleanup easy.
+  // We construct the result directly into `out`, so terminate the list.
   *out = nullptr;
 
-  // The kernel keeps packets under 8KiB (NLMSG_GOODSIZE),
-  // but that's a bit too large to go on the stack.
-  size_t buf_len = 8192;
-  char* buf = new char[buf_len];
-  if (buf == nullptr) return -1;
-
   // Open the netlink socket and ask for all the links and addresses.
-  int fd = socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_ROUTE);
-  bool okay = fd != -1 &&
-      __send_netlink_request(fd, RTM_GETLINK) && __read_netlink_responses(fd, out, buf, buf_len) &&
-      __send_netlink_request(fd, RTM_GETADDR) && __read_netlink_responses(fd, out, buf, buf_len);
-
+  NetlinkConnection nc;
+  bool okay = nc.SendRequest(RTM_GETLINK) && nc.ReadResponses(__getifaddrs_callback, out) &&
+              nc.SendRequest(RTM_GETADDR) && nc.ReadResponses(__getifaddrs_callback, out);
   if (!okay) {
     freeifaddrs(*out);
     // Ensure that callers crash if they forget to check for success.
     *out = nullptr;
+    return -1;
   }
-  {
-    int saved_errno = errno;
-    close(fd);
-    delete[] buf;
-    errno = saved_errno;
-  }
-  return okay ? 0 : -1;
+
+  return 0;
 }
 
 void freeifaddrs(ifaddrs* list) {
diff --git a/libc/bionic/net_if.cpp b/libc/bionic/net_if.cpp
new file mode 100644
index 0000000..a6b896e
--- /dev/null
+++ b/libc/bionic/net_if.cpp
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <linux/if_packet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/sockios.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "private/ErrnoRestorer.h"
+
+#include "bionic_netlink.h"
+
+char* if_indextoname(unsigned ifindex, char* ifname) {
+  int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+  if (s == -1) return nullptr;
+
+  struct ifreq ifr = {};
+  ifr.ifr_ifindex = ifindex;
+
+  int rc = ioctl(s, SIOCGIFNAME, &ifr);
+  ErrnoRestorer errno_restorer;
+  close(s);
+  return (rc == -1) ? nullptr : strncpy(ifname, ifr.ifr_name, IFNAMSIZ);
+}
+
+unsigned if_nametoindex(const char* ifname) {
+  int s = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0);
+  if (s == -1) return 0;
+
+  struct ifreq ifr = {};
+  strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+  ifr.ifr_name[IFNAMSIZ - 1] = 0;
+
+  int rc = ioctl(s, SIOCGIFINDEX, &ifr);
+  ErrnoRestorer errno_restorer;
+  close(s);
+  return (rc == -1) ? 0 : ifr.ifr_ifindex;
+}
+
+struct if_list {
+  if_list* next;
+  struct if_nameindex data;
+
+  if_list(if_list** list) {
+    // push_front onto `list`.
+    next = *list;
+    *list = this;
+  }
+
+  static void Free(if_list* list, bool names_too) {
+    while (list) {
+      if_list* it = list;
+      list = it->next;
+      if (names_too) free(it->data.if_name);
+      free(it);
+    }
+  }
+};
+
+static void __if_nameindex_callback(void* context, nlmsghdr* hdr) {
+  if_list** list = reinterpret_cast<if_list**>(context);
+  if (hdr->nlmsg_type == RTM_NEWLINK) {
+    ifinfomsg* ifi = reinterpret_cast<ifinfomsg*>(NLMSG_DATA(hdr));
+
+    // Create a new entry and set the interface index.
+    if_list* new_link = new if_list(list);
+    new_link->data.if_index = ifi->ifi_index;
+
+    // Go through the various bits of information and find the name.
+    rtattr* rta = IFLA_RTA(ifi);
+    size_t rta_len = IFLA_PAYLOAD(hdr);
+    while (RTA_OK(rta, rta_len)) {
+      if (rta->rta_type == IFLA_IFNAME) {
+        new_link->data.if_name = strndup(reinterpret_cast<char*>(RTA_DATA(rta)), RTA_PAYLOAD(rta));
+      }
+      rta = RTA_NEXT(rta, rta_len);
+    }
+  }
+}
+
+struct if_nameindex* if_nameindex() {
+  if_list* list = nullptr;
+
+  // Open the netlink socket and ask for all the links;
+  NetlinkConnection nc;
+  bool okay = nc.SendRequest(RTM_GETLINK) && nc.ReadResponses(__if_nameindex_callback, &list);
+  if (!okay) {
+    if_list::Free(list, true);
+    return nullptr;
+  }
+
+  // Count the interfaces.
+  size_t interface_count = 0;
+  for (if_list* it = list; it != nullptr; it = it->next) {
+    ++interface_count;
+  }
+
+  // Build the array POSIX requires us to return.
+  struct if_nameindex* result = new struct if_nameindex[interface_count + 1];
+  if (result) {
+    struct if_nameindex* out = result;
+    for (if_list* it = list; it != nullptr; it = it->next) {
+      out->if_index = it->data.if_index;
+      out->if_name = it->data.if_name;
+      ++out;
+    }
+    out->if_index = 0;
+    out->if_name = nullptr;
+  }
+
+  // Free temporary storage.
+  if_list::Free(list, false);
+
+  return result;
+}
+
+void if_freenameindex(struct if_nameindex* array) {
+  if (array == nullptr) return;
+
+  struct if_nameindex* ptr = array;
+  while (ptr->if_index != 0 || ptr->if_name != nullptr) {
+    free(ptr->if_name);
+    ++ptr;
+  }
+
+  delete[] array;
+}
diff --git a/libc/include/net/if.h b/libc/include/net/if.h
index 0efbf7f..aa4c19e 100644
--- a/libc/include/net/if.h
+++ b/libc/include/net/if.h
@@ -39,11 +39,15 @@
 
 __BEGIN_DECLS
 
-/*
- * Map an interface name into its corresponding index.
- */
-extern unsigned int if_nametoindex(const char *);
-extern char*        if_indextoname(unsigned ifindex, char *ifname);
+struct if_nameindex {
+  unsigned if_index;
+  char* if_name;
+};
+
+char* if_indextoname(unsigned, char*);
+unsigned if_nametoindex(const char*);
+struct if_nameindex* if_nameindex(void);
+void if_freenameindex(struct if_nameindex*);
 
 __END_DECLS
 
diff --git a/libc/libc.arm.brillo.map b/libc/libc.arm.brillo.map
index c51b7e1..9cef643 100644
--- a/libc/libc.arm.brillo.map
+++ b/libc/libc.arm.brillo.map
@@ -1236,6 +1236,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.arm.map b/libc/libc.arm.map
index 6297fd1..c9f8708 100644
--- a/libc/libc.arm.map
+++ b/libc/libc.arm.map
@@ -1237,6 +1237,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.arm64.map b/libc/libc.arm64.map
index fea1759..e5933ae 100644
--- a/libc/libc.arm64.map
+++ b/libc/libc.arm64.map
@@ -1157,6 +1157,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     pthread_barrierattr_destroy;
diff --git a/libc/libc.map.txt b/libc/libc.map.txt
index 258ff48..b1322a2 100644
--- a/libc/libc.map.txt
+++ b/libc/libc.map.txt
@@ -1264,6 +1264,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.mips.brillo.map b/libc/libc.mips.brillo.map
index 67e1c05..965927a 100644
--- a/libc/libc.mips.brillo.map
+++ b/libc/libc.mips.brillo.map
@@ -1221,6 +1221,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.mips.map b/libc/libc.mips.map
index b82ef0f..59ea26b 100644
--- a/libc/libc.mips.map
+++ b/libc/libc.mips.map
@@ -1222,6 +1222,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.mips64.map b/libc/libc.mips64.map
index fea1759..e5933ae 100644
--- a/libc/libc.mips64.map
+++ b/libc/libc.mips64.map
@@ -1157,6 +1157,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     pthread_barrierattr_destroy;
diff --git a/libc/libc.x86.brillo.map b/libc/libc.x86.brillo.map
index 71390b7..c19e797 100644
--- a/libc/libc.x86.brillo.map
+++ b/libc/libc.x86.brillo.map
@@ -1220,6 +1220,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.x86.map b/libc/libc.x86.map
index 6905ff8..51f617e 100644
--- a/libc/libc.x86.map
+++ b/libc/libc.x86.map
@@ -1221,6 +1221,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     prlimit; # arm mips x86
diff --git a/libc/libc.x86_64.map b/libc/libc.x86_64.map
index fea1759..e5933ae 100644
--- a/libc/libc.x86_64.map
+++ b/libc/libc.x86_64.map
@@ -1157,6 +1157,8 @@
     getgrgid_r;
     getgrnam_r;
     getifaddrs;
+    if_freenameindex;
+    if_nameindex;
     preadv;
     preadv64;
     pthread_barrierattr_destroy;
diff --git a/tests/ifaddrs_test.cpp b/tests/ifaddrs_test.cpp
index 0c32332..8c45947 100644
--- a/tests/ifaddrs_test.cpp
+++ b/tests/ifaddrs_test.cpp
@@ -162,7 +162,7 @@
 
 static void print_sockaddr_ll(const char* what, const sockaddr* p) {
   const sockaddr_ll* s = reinterpret_cast<const sockaddr_ll*>(p);
-  printf("\t%s\t", what);
+  printf("\t\t%s\t", what);
   for (int i = 0; i < s->sll_halen; ++i) {
     if (i > 0) printf(":");
     printf("%02X", s->sll_addr[i]);
@@ -180,7 +180,7 @@
     printf("%d getnameinfo() failed: %s\n", family, gai_strerror(error));
     strcpy(host, "???");
   }
-  printf("\t%s: <%s>\n", what, host);
+  printf("\t\t%s: <%s>\n", what, host);
 }
 
 static const char* family_to_name(int family) {
@@ -200,7 +200,8 @@
     int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family :
                                  ifa->ifa_broadaddr ? ifa->ifa_broadaddr->sa_family : AF_UNSPEC;
 
-    printf("%s\n\t%s (%d) flags=%#x\n",
+    printf("\t%s\n"
+           "\t\t%s (%d) flags=%#x\n",
            ifa->ifa_name, family_to_name(family), family, ifa->ifa_flags);
 
     if (family == AF_PACKET) {
diff --git a/tests/net_if_test.cpp b/tests/net_if_test.cpp
index 9f38411..caaed5f 100644
--- a/tests/net_if_test.cpp
+++ b/tests/net_if_test.cpp
@@ -17,6 +17,7 @@
 #include <net/if.h>
 
 #include <errno.h>
+#include <ifaddrs.h>
 
 #include <gtest/gtest.h>
 
@@ -34,3 +35,39 @@
   unsigned index = if_nametoindex("this-interface-does-not-exist");
   ASSERT_EQ(0U, index);
 }
+
+TEST(net_if, if_nameindex) {
+  struct if_nameindex* list = if_nameindex();
+  ASSERT_TRUE(list != nullptr);
+
+  ASSERT_TRUE(list->if_index != 0);
+
+  std::set<std::string> if_nameindex_names;
+  char buf[IF_NAMESIZE] = {};
+  bool saw_lo = false;
+  for (struct if_nameindex* it = list; it->if_index != 0; ++it) {
+    fprintf(stderr, "\t%d\t%s\n", it->if_index, it->if_name);
+    if_nameindex_names.insert(it->if_name);
+    EXPECT_EQ(it->if_index, if_nametoindex(it->if_name));
+    EXPECT_STREQ(it->if_name, if_indextoname(it->if_index, buf));
+    if (strcmp(it->if_name, "lo") == 0) saw_lo = true;
+  }
+  ASSERT_TRUE(saw_lo);
+  if_freenameindex(list);
+
+  std::set<std::string> getifaddrs_names;
+  ifaddrs* ifa;
+  ASSERT_EQ(0, getifaddrs(&ifa));
+  for (ifaddrs* it = ifa; it != nullptr; it = it->ifa_next) {
+    getifaddrs_names.insert(it->ifa_name);
+  }
+  freeifaddrs(ifa);
+
+  ASSERT_EQ(getifaddrs_names, if_nameindex_names);
+}
+
+TEST(net_if, if_freenameindex_nullptr) {
+#if defined(__BIONIC__)
+  if_freenameindex(nullptr);
+#endif
+}