Extend libnetdevice for Netlink Proxy

Add some additional generic send/receive functionality for
NetlinkSocket.

Bug: 155190864
Test: Manual
Change-Id: I7a882fa642553c61e0b2b3a32638a309089c6d22
diff --git a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
index 6146d5e..633ed82 100644
--- a/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
+++ b/automotive/can/1.0/default/libnetdevice/NetlinkSocket.cpp
@@ -27,7 +27,8 @@
  */
 static constexpr bool kSuperVerbose = false;
 
-NetlinkSocket::NetlinkSocket(int protocol) : mProtocol(protocol) {
+NetlinkSocket::NetlinkSocket(int protocol, unsigned int pid, uint32_t groups)
+    : mProtocol(protocol) {
     mFd.reset(socket(AF_NETLINK, SOCK_RAW, protocol));
     if (!mFd.ok()) {
         PLOG(ERROR) << "Can't open Netlink socket";
@@ -35,21 +36,23 @@
         return;
     }
 
-    struct sockaddr_nl sa = {};
+    sockaddr_nl sa = {};
     sa.nl_family = AF_NETLINK;
+    sa.nl_pid = pid;
+    sa.nl_groups = groups;
 
-    if (bind(mFd.get(), reinterpret_cast<struct sockaddr*>(&sa), sizeof(sa)) < 0) {
+    if (bind(mFd.get(), reinterpret_cast<sockaddr*>(&sa), sizeof(sa)) < 0) {
         PLOG(ERROR) << "Can't bind Netlink socket";
         mFd.reset();
         mFailed = true;
     }
 }
 
-bool NetlinkSocket::send(struct nlmsghdr* nlmsg, size_t totalLen) {
+bool NetlinkSocket::send(nlmsghdr* nlmsg, size_t totalLen) {
     if constexpr (kSuperVerbose) {
         nlmsg->nlmsg_seq = mSeq;
-        LOG(VERBOSE) << (mFailed ? "(not)" : "")
-                     << "sending Netlink message: " << toString(nlmsg, totalLen, mProtocol);
+        LOG(VERBOSE) << (mFailed ? "(not) " : "")
+                     << "sending Netlink message: " << toString({nlmsg, totalLen}, mProtocol);
     }
 
     if (mFailed) return false;
@@ -58,12 +61,12 @@
     nlmsg->nlmsg_seq = mSeq++;
     nlmsg->nlmsg_flags |= NLM_F_ACK;
 
-    struct iovec iov = {nlmsg, nlmsg->nlmsg_len};
+    iovec iov = {nlmsg, nlmsg->nlmsg_len};
 
-    struct sockaddr_nl sa = {};
+    sockaddr_nl sa = {};
     sa.nl_family = AF_NETLINK;
 
-    struct msghdr msg = {};
+    msghdr msg = {};
     msg.msg_name = &sa;
     msg.msg_namelen = sizeof(sa);
     msg.msg_iov = &iov;
@@ -76,15 +79,65 @@
     return true;
 }
 
+bool NetlinkSocket::send(const nlbuf<nlmsghdr>& msg, const sockaddr_nl& sa) {
+    if constexpr (kSuperVerbose) {
+        LOG(VERBOSE) << (mFailed ? "(not) " : "")
+                     << "sending Netlink message: " << toString(msg, mProtocol);
+    }
+
+    if (mFailed) return false;
+    const auto rawMsg = msg.getRaw();
+    const auto bytesSent = sendto(mFd.get(), rawMsg.ptr(), rawMsg.len(), 0,
+                                  reinterpret_cast<const sockaddr*>(&sa), sizeof(sa));
+    if (bytesSent < 0) {
+        PLOG(ERROR) << "Can't send Netlink message";
+        return false;
+    }
+    return true;
+}
+
+std::optional<nlbuf<nlmsghdr>> NetlinkSocket::receive(void* buf, size_t bufLen) {
+    sockaddr_nl sa = {};
+    return receive(buf, bufLen, sa);
+}
+
+std::optional<nlbuf<nlmsghdr>> NetlinkSocket::receive(void* buf, size_t bufLen, sockaddr_nl& sa) {
+    if (mFailed) return std::nullopt;
+
+    socklen_t saLen = sizeof(sa);
+    if (bufLen == 0) {
+        LOG(ERROR) << "Receive buffer has zero size!";
+        return std::nullopt;
+    }
+    const auto bytesReceived =
+            recvfrom(mFd.get(), buf, bufLen, MSG_TRUNC, reinterpret_cast<sockaddr*>(&sa), &saLen);
+    if (bytesReceived <= 0) {
+        PLOG(ERROR) << "Failed to receive Netlink message";
+        return std::nullopt;
+    } else if (unsigned(bytesReceived) > bufLen) {
+        PLOG(ERROR) << "Received data larger than the receive buffer! " << bytesReceived << " > "
+                    << bufLen;
+        return std::nullopt;
+    }
+
+    nlbuf<nlmsghdr> msg(reinterpret_cast<nlmsghdr*>(buf), bytesReceived);
+    if constexpr (kSuperVerbose) {
+        LOG(VERBOSE) << "received " << toString(msg, mProtocol);
+    }
+    return msg;
+}
+
+/* TODO(161389935): Migrate receiveAck to use nlmsg<> internally. Possibly reuse
+ * NetlinkSocket::receive(). */
 bool NetlinkSocket::receiveAck() {
     if (mFailed) return false;
 
     char buf[8192];
 
-    struct sockaddr_nl sa;
-    struct iovec iov = {buf, sizeof(buf)};
+    sockaddr_nl sa;
+    iovec iov = {buf, sizeof(buf)};
 
-    struct msghdr msg = {};
+    msghdr msg = {};
     msg.msg_name = &sa;
     msg.msg_namelen = sizeof(sa);
     msg.msg_iov = &iov;
@@ -102,11 +155,11 @@
         return false;
     }
 
-    for (auto nlmsg = reinterpret_cast<struct nlmsghdr*>(buf); NLMSG_OK(nlmsg, remainingLen);
+    for (auto nlmsg = reinterpret_cast<nlmsghdr*>(buf); NLMSG_OK(nlmsg, remainingLen);
          nlmsg = NLMSG_NEXT(nlmsg, remainingLen)) {
         if constexpr (kSuperVerbose) {
             LOG(VERBOSE) << "received Netlink response: "
-                         << toString(nlmsg, sizeof(buf), mProtocol);
+                         << toString({nlmsg, nlmsg->nlmsg_len}, mProtocol);
         }
 
         // We're looking for error/ack message only, ignoring others.
@@ -116,7 +169,7 @@
         }
 
         // Found error/ack message, return status.
-        auto nlerr = reinterpret_cast<struct nlmsgerr*>(NLMSG_DATA(nlmsg));
+        const auto nlerr = reinterpret_cast<nlmsgerr*>(NLMSG_DATA(nlmsg));
         if (nlerr->error != 0) {
             LOG(ERROR) << "Received Netlink error message: " << nlerr->error;
             return false;
@@ -127,4 +180,14 @@
     return false;
 }
 
+std::optional<unsigned int> NetlinkSocket::getSocketPid() {
+    sockaddr_nl sa = {};
+    socklen_t sasize = sizeof(sa);
+    if (getsockname(mFd.get(), reinterpret_cast<sockaddr*>(&sa), &sasize) < 0) {
+        PLOG(ERROR) << "Failed to getsockname() for netlink_fd!";
+        return std::nullopt;
+    }
+    return sa.nl_pid;
+}
+
 }  // namespace android::netdevice
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/NetlinkSocket.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/NetlinkSocket.h
index 8900ff7..826b6b8 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/NetlinkSocket.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/NetlinkSocket.h
@@ -19,9 +19,12 @@
 #include <android-base/macros.h>
 #include <android-base/unique_fd.h>
 #include <libnetdevice/NetlinkRequest.h>
+#include <libnetdevice/nlbuf.h>
 
 #include <linux/netlink.h>
 
+#include <optional>
+
 namespace android::netdevice {
 
 /**
@@ -31,12 +34,23 @@
  * use multiple instances over multiple threads.
  */
 struct NetlinkSocket {
-    NetlinkSocket(int protocol);
+    /**
+     * NetlinkSocket constructor.
+     *
+     * \param protocol the Netlink protocol to use.
+     * \param pid port id. Default value of 0 allows the kernel to assign us a unique pid. (NOTE:
+     * this is NOT the same as process id!)
+     * \param groups Netlink multicast groups to listen to. This is a 32-bit bitfield, where each
+     * bit is a different group. Default value of 0 means no groups are selected. See man netlink.7
+     * for more details.
+     */
+    NetlinkSocket(int protocol, unsigned int pid = 0, uint32_t groups = 0);
 
     /**
-     * Send Netlink message to Kernel.
+     * Send Netlink message to Kernel. The sequence number will be automatically incremented, and
+     * the NLM_F_ACK (request ACK) flag will be set.
      *
-     * \param msg Message to send, nlmsg_seq will be set to next sequence number
+     * \param msg Message to send.
      * \return true, if succeeded
      */
     template <class T, unsigned int BUFSIZE>
@@ -46,12 +60,47 @@
     }
 
     /**
+     * Send Netlink message. The message will be sent as is, without any modification.
+     *
+     * \param msg Message to send.
+     * \param sa Destination address.
+     * \return true, if succeeded
+     */
+    bool send(const nlbuf<nlmsghdr>& msg, const sockaddr_nl& sa);
+
+    /**
+     * Receive Netlink data.
+     *
+     * \param buf buffer to hold message data.
+     * \param bufLen length of buf.
+     * \return nlbuf with message data, std::nullopt on error.
+     */
+    std::optional<nlbuf<nlmsghdr>> receive(void* buf, size_t bufLen);
+
+    /**
+     * Receive Netlink data with address info.
+     *
+     * \param buf buffer to hold message data.
+     * \param bufLen length of buf.
+     * \param sa Blank struct that recvfrom will populate with address info.
+     * \return nlbuf with message data, std::nullopt on error.
+     */
+    std::optional<nlbuf<nlmsghdr>> receive(void* buf, size_t bufLen, sockaddr_nl& sa);
+
+    /**
      * Receive Netlink ACK message from Kernel.
      *
      * \return true if received ACK message, false in case of error
      */
     bool receiveAck();
 
+    /**
+     * Gets the PID assigned to mFd.
+     *
+     * \return pid that mSocket is bound to.
+     */
+    std::optional<unsigned int> getSocketPid();
+
   private:
     const int mProtocol;
 
@@ -59,7 +108,7 @@
     base::unique_fd mFd;
     bool mFailed = false;
 
-    bool send(struct nlmsghdr* msg, size_t totalLen);
+    bool send(nlmsghdr* msg, size_t totalLen);
 
     DISALLOW_COPY_AND_ASSIGN(NetlinkSocket);
 };
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/nlbuf.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/nlbuf.h
index f7e53be..17815f9 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/nlbuf.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/nlbuf.h
@@ -53,6 +53,12 @@
     static constexpr size_t hdrlen = align(sizeof(T));
 
   public:
+    /**
+     * Constructor for nlbuf.
+     *
+     * \param data A pointer to the data the nlbuf wraps.
+     * \param bufferLen Length of buffer.
+     */
     nlbuf(const T* data, size_t bufferLen) : mData(data), mBufferEnd(pointerAdd(data, bufferLen)) {}
 
     const T* operator->() const {
diff --git a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/printer.h b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/printer.h
index efeb6b1..071fa63 100644
--- a/automotive/can/1.0/default/libnetdevice/include/libnetdevice/printer.h
+++ b/automotive/can/1.0/default/libnetdevice/include/libnetdevice/printer.h
@@ -16,12 +16,22 @@
 
 #pragma once
 
+#include <libnetdevice/nlbuf.h>
+
 #include <linux/netlink.h>
 
 #include <string>
 
 namespace android::netdevice {
 
-std::string toString(const nlmsghdr* hdr, size_t bufLen, int protocol);
+/**
+ * Stringify a Netlink message.
+ *
+ * \param hdr Pointer to the message(s) to print.
+ * \param protocol Which Netlink protocol hdr uses.
+ * \param printPayload True will stringify message data, false will only stringify the header(s).
+ * \return Stringified message.
+ */
+std::string toString(const nlbuf<nlmsghdr> hdr, int protocol, bool printPayload = false);
 
 }  // namespace android::netdevice
diff --git a/automotive/can/1.0/default/libnetdevice/printer.cpp b/automotive/can/1.0/default/libnetdevice/printer.cpp
index a8715da..0076dd6 100644
--- a/automotive/can/1.0/default/libnetdevice/printer.cpp
+++ b/automotive/can/1.0/default/libnetdevice/printer.cpp
@@ -62,13 +62,20 @@
 }
 
 static void toStream(std::stringstream& ss, const nlbuf<uint8_t> data) {
+    const auto rawData = data.getRaw();
+    const auto dataLen = rawData.len();
     ss << std::hex;
+    if (dataLen > 16) ss << std::endl << " 0000 ";
     int i = 0;
-    for (const auto byte : data.getRaw()) {
+    for (const auto byte : rawData) {
         if (i++ > 0) ss << ' ';
         ss << std::setw(2) << unsigned(byte);
+        if (i % 16 == 0) {
+            ss << std::endl << ' ' << std::dec << std::setw(4) << i << std::hex;
+        }
     }
     ss << std::dec;
+    if (dataLen > 16) ss << std::endl;
 }
 
 static void toStream(std::stringstream& ss, const nlbuf<nlattr> attr,
@@ -105,7 +112,7 @@
     }
 }
 
-static std::string toString(const nlbuf<nlmsghdr> hdr, int protocol) {
+std::string toString(const nlbuf<nlmsghdr> hdr, int protocol, bool printPayload) {
     if (!hdr.firstOk()) return "nlmsg{buffer overflow}";
 
     std::stringstream ss;
@@ -133,10 +140,13 @@
     }
     if (hdr->nlmsg_seq != 0) ss << ", seq=" << hdr->nlmsg_seq;
     if (hdr->nlmsg_pid != 0) ss << ", pid=" << hdr->nlmsg_pid;
+    ss << ", len=" << hdr->nlmsg_len;
 
     ss << ", crc=" << std::hex << std::setw(4) << crc16(hdr.data<uint8_t>()) << std::dec;
     ss << "} ";
 
+    if (!printPayload) return ss.str();
+
     if (!msgDescMaybe.has_value()) {
         toStream(ss, hdr.data<uint8_t>());
     } else {
@@ -161,8 +171,4 @@
     return ss.str();
 }
 
-std::string toString(const nlmsghdr* hdr, size_t bufLen, int protocol) {
-    return toString({hdr, bufLen}, protocol);
-}
-
 }  // namespace android::netdevice
diff --git a/automotive/can/1.0/default/libnetdevice/protocols/common/Error.cpp b/automotive/can/1.0/default/libnetdevice/protocols/common/Error.cpp
index d42a50a..25ae680 100644
--- a/automotive/can/1.0/default/libnetdevice/protocols/common/Error.cpp
+++ b/automotive/can/1.0/default/libnetdevice/protocols/common/Error.cpp
@@ -30,7 +30,7 @@
 
 void Error::toStream(std::stringstream& ss, const nlmsgerr& data) const {
     ss << "nlmsgerr{error=" << data.error
-       << ", msg=" << toString(&data.msg, sizeof(data.msg), mProtocol) << "}";
+       << ", msg=" << toString({&data.msg, sizeof(data.msg)}, mProtocol) << "}";
 }
 
 }  // namespace android::netdevice::protocols::base