Move the netlink command code to a new NetlinkCommands file.

Test: bullhead builds, netd boots
Test: netd_{unit,integration}_test pass
Bug: 34873832
Change-Id: Ia6fcde63e1092a62cad1c5238bbb9a91a9f39080
diff --git a/server/NetlinkCommands.cpp b/server/NetlinkCommands.cpp
new file mode 100644
index 0000000..b580d09
--- /dev/null
+++ b/server/NetlinkCommands.cpp
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2017 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 <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+#define LOG_TAG "Netd"
+#include <cutils/log.h>
+
+#include "NetdConstants.h"
+#include "NetlinkCommands.h"
+
+namespace android {
+namespace net {
+
+int openRtNetlinkSocket() {
+    int sock = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_ROUTE);
+    if (sock == -1) {
+        return -errno;
+    }
+    if (connect(sock, reinterpret_cast<const sockaddr*>(&KERNEL_NLADDR),
+                sizeof(KERNEL_NLADDR)) == -1) {
+        return -errno;
+    }
+    return sock;
+}
+
+int recvNetlinkAck(int sock) {
+    struct {
+        nlmsghdr msg;
+        nlmsgerr err;
+    } response;
+
+    int ret = recv(sock, &response, sizeof(response), 0);
+
+    if (ret == -1) {
+        ret = -errno;
+        ALOGE("netlink recv failed (%s)", strerror(-ret));
+        return ret;
+    }
+
+    if (ret != sizeof(response)) {
+        ALOGE("bad netlink response message size (%d != %zu)", ret, sizeof(response));
+        return -EBADMSG;
+    }
+
+    return response.err.error;  // Netlink errors are negative errno.
+}
+
+// Sends a netlink request and possibly expects an ack.
+// |iov| is an array of struct iovec that contains the netlink message payload.
+// The netlink header is generated by this function based on |action| and |flags|.
+// Returns -errno if there was an error or if the kernel reported an error.
+#ifdef __clang__
+#if __has_feature(address_sanitizer)
+__attribute__((optnone))
+#endif
+#endif
+WARN_UNUSED_RESULT int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen,
+                                          const NetlinkDumpCallback& callback) {
+    nlmsghdr nlmsg = {
+        .nlmsg_type = action,
+        .nlmsg_flags = flags,
+    };
+    iov[0].iov_base = &nlmsg;
+    iov[0].iov_len = sizeof(nlmsg);
+    for (int i = 0; i < iovlen; ++i) {
+        nlmsg.nlmsg_len += iov[i].iov_len;
+    }
+
+    int sock = openRtNetlinkSocket();
+    if (sock < 0) {
+        return sock;
+    }
+
+
+    int ret = 0;
+
+    if (writev(sock, iov, iovlen) == -1) {
+        ret = -errno;
+        ALOGE("netlink socket connect/writev failed (%s)", strerror(-ret));
+        close(sock);
+        return ret;
+    }
+
+    if (flags & NLM_F_ACK) {
+        ret = recvNetlinkAck(sock);
+    }
+
+    if ((flags & NLM_F_DUMP) && callback != nullptr) {
+        ret = processNetlinkDump(sock, callback);
+    }
+
+    close(sock);
+
+    return ret;
+}
+
+int sendNetlinkRequest(uint16_t action, uint16_t flags, iovec* iov, int iovlen) {
+    return sendNetlinkRequest(action, flags, iov, iovlen, nullptr);
+}
+
+int processNetlinkDump(int sock, const NetlinkDumpCallback& callback) {
+    char buf[kNetlinkDumpBufferSize];
+
+    ssize_t bytesread;
+    do {
+        bytesread = read(sock, buf, sizeof(buf));
+
+        if (bytesread < 0) {
+            return -errno;
+        }
+
+        uint32_t len = bytesread;
+        for (nlmsghdr *nlh = reinterpret_cast<nlmsghdr *>(buf);
+             NLMSG_OK(nlh, len);
+             nlh = NLMSG_NEXT(nlh, len)) {
+            switch (nlh->nlmsg_type) {
+              case NLMSG_DONE:
+                callback(nullptr);
+                return 0;
+              case NLMSG_ERROR: {
+                nlmsgerr *err = reinterpret_cast<nlmsgerr *>(NLMSG_DATA(nlh));
+                return err->error;
+              }
+              default:
+                callback(nlh);
+            }
+        }
+    } while (bytesread > 0);
+
+    return 0;
+}
+
+}  // namespace net
+}  // namespace android