Merge "Extend to receive NFLOG packets."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e78fc88..7f9536e 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -54,3 +54,4 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/default.prop)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/default.prop)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
diff --git a/include/cutils/uevent.h b/include/cutils/uevent.h
index 4cca7e5..da1c2aa 100644
--- a/include/cutils/uevent.h
+++ b/include/cutils/uevent.h
@@ -27,6 +27,7 @@
 int uevent_open_socket(int buf_sz, bool passcred);
 ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length);
 ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid);
+ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid);
 
 #ifdef __cplusplus
 }
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
index c345cdb..4fa49c5 100644
--- a/include/sysutils/NetlinkEvent.h
+++ b/include/sysutils/NetlinkEvent.h
@@ -57,6 +57,7 @@
     bool parseIfInfoMessage(const struct nlmsghdr *nh);
     bool parseIfAddrMessage(const struct nlmsghdr *nh);
     bool parseUlogPacketMessage(const struct nlmsghdr *nh);
+    bool parseNfPacketMessage(struct nlmsghdr *nh);
     bool parseRtMessage(const struct nlmsghdr *nh);
     bool parseNdUserOptMessage(const struct nlmsghdr *nh);
 };
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
index 6e52c3b..82465d6 100644
--- a/include/sysutils/NetlinkListener.h
+++ b/include/sysutils/NetlinkListener.h
@@ -27,6 +27,7 @@
 public:
     static const int NETLINK_FORMAT_ASCII = 0;
     static const int NETLINK_FORMAT_BINARY = 1;
+    static const int NETLINK_FORMAT_BINARY_UNICAST = 2;
 
 #if 1
     /* temporary version until we can get Motorola to update their
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
index 97a81e3..827170a 100644
--- a/libcutils/uevent.c
+++ b/libcutils/uevent.c
@@ -31,12 +31,12 @@
  */
 ssize_t uevent_kernel_multicast_recv(int socket, void *buffer, size_t length)
 {
-    uid_t user = -1;
-    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &user);
+    uid_t uid = -1;
+    return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
 }
 
 /**
- * Like the above, but passes a uid_t in by reference. In the event that this
+ * Like the above, but passes a uid_t in by pointer. In the event that this
  * fails due to a bad uid check, the uid_t will be set to the uid of the
  * socket's peer.
  *
@@ -44,8 +44,12 @@
  * returns -1, sets errno to EIO, and sets "user" to the UID associated with the
  * message. If the peer UID cannot be determined, "user" is set to -1."
  */
-ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer,
-                                         size_t length, uid_t *user)
+ssize_t uevent_kernel_multicast_uid_recv(int socket, void *buffer, size_t length, uid_t *uid)
+{
+    return uevent_kernel_recv(socket, buffer, length, true, uid);
+}
+
+ssize_t uevent_kernel_recv(int socket, void *buffer, size_t length, bool require_group, uid_t *uid)
 {
     struct iovec iov = { buffer, length };
     struct sockaddr_nl addr;
@@ -60,7 +64,7 @@
         0,
     };
 
-    *user = -1;
+    *uid = -1;
     ssize_t n = recvmsg(socket, &hdr, 0);
     if (n <= 0) {
         return n;
@@ -73,14 +77,18 @@
     }
 
     struct ucred *cred = (struct ucred *)CMSG_DATA(cmsg);
-    *user = cred->uid;
+    *uid = cred->uid;
     if (cred->uid != 0) {
         /* ignoring netlink message from non-root user */
         goto out;
     }
 
-    if (addr.nl_groups == 0 || addr.nl_pid != 0) {
-        /* ignoring non-kernel or unicast netlink message */
+    if (addr.nl_pid != 0) {
+        /* ignore non-kernel */
+        goto out;
+    }
+    if (require_group && addr.nl_groups == 0) {
+        /* ignore unicast messages when requested */
         goto out;
     }
 
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
index 246f954..b902a81 100644
--- a/libsysutils/Android.mk
+++ b/libsysutils/Android.mk
@@ -16,11 +16,12 @@
 
 LOCAL_MODULE:= libsysutils
 
-LOCAL_C_INCLUDES :=
-
 LOCAL_CFLAGS := -Werror
 
-LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_SHARED_LIBRARIES := \
+        libcutils \
+        liblog \
+        libnl
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 9d596ef..909df86 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -32,13 +32,21 @@
 #include <linux/if_addr.h>
 #include <linux/if_link.h>
 #include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
 #include <linux/netfilter_ipv4/ipt_ULOG.h>
+
 /* From kernel's net/netfilter/xt_quota2.c */
-const int QLOG_NL_EVENT  = 112;
+const int LOCAL_QLOG_NL_EVENT = 112;
+const int LOCAL_NFLOG_PACKET = NFNL_SUBSYS_ULOG << 8 | NFULNL_MSG_PACKET;
 
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
 
+#include <netlink/attr.h>
+#include <netlink/genl/genl.h>
+#include <netlink/handlers.h>
+#include <netlink/msg.h>
+
 const int NetlinkEvent::NlActionUnknown = 0;
 const int NetlinkEvent::NlActionAdd = 1;
 const int NetlinkEvent::NlActionRemove = 2;
@@ -95,7 +103,8 @@
         NL_EVENT_RTM_NAME(RTM_NEWROUTE);
         NL_EVENT_RTM_NAME(RTM_DELROUTE);
         NL_EVENT_RTM_NAME(RTM_NEWNDUSEROPT);
-        NL_EVENT_RTM_NAME(QLOG_NL_EVENT);
+        NL_EVENT_RTM_NAME(LOCAL_QLOG_NL_EVENT);
+        NL_EVENT_RTM_NAME(LOCAL_NFLOG_PACKET);
         default:
             return NULL;
     }
@@ -272,6 +281,41 @@
 }
 
 /*
+ * Parse a LOCAL_NFLOG_PACKET message.
+ */
+bool NetlinkEvent::parseNfPacketMessage(struct nlmsghdr *nh) {
+    int uid = -1;
+    int len = 0;
+    char* raw = NULL;
+
+    struct nlattr *uid_attr = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_UID);
+    if (uid_attr) {
+        uid = ntohl(nla_get_u32(uid_attr));
+    }
+
+    struct nlattr *payload = nlmsg_find_attr(nh, sizeof(struct genlmsghdr), NFULA_PAYLOAD);
+    if (payload) {
+        /* First 256 bytes is plenty */
+        len = nla_len(payload);
+        if (len > 256) len = 256;
+        raw = (char*) nla_data(payload);
+    }
+
+    char* hex = (char*) calloc(1, 5 + (len * 2));
+    strcpy(hex, "HEX=");
+    for (int i = 0; i < len; i++) {
+        hex[4 + (i * 2)] = "0123456789abcdef"[(raw[i] >> 4) & 0xf];
+        hex[5 + (i * 2)] = "0123456789abcdef"[raw[i] & 0xf];
+    }
+
+    asprintf(&mParams[0], "UID=%d", uid);
+    mParams[1] = hex;
+    mSubsystem = strdup("strict");
+    mAction = NlActionChange;
+    return true;
+}
+
+/*
  * Parse a RTM_NEWROUTE or RTM_DELROUTE message.
  */
 bool NetlinkEvent::parseRtMessage(const struct nlmsghdr *nh) {
@@ -478,7 +522,7 @@
  * TODO: consider only ever looking at the first message.
  */
 bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
-    const struct nlmsghdr *nh;
+    struct nlmsghdr *nh;
 
     for (nh = (struct nlmsghdr *) buffer;
          NLMSG_OK(nh, (unsigned) size) && (nh->nlmsg_type != NLMSG_DONE);
@@ -493,7 +537,7 @@
             if (parseIfInfoMessage(nh))
                 return true;
 
-        } else if (nh->nlmsg_type == QLOG_NL_EVENT) {
+        } else if (nh->nlmsg_type == LOCAL_QLOG_NL_EVENT) {
             if (parseUlogPacketMessage(nh))
                 return true;
 
@@ -511,6 +555,10 @@
             if (parseNdUserOptMessage(nh))
                 return true;
 
+        } else if (nh->nlmsg_type == LOCAL_NFLOG_PACKET) {
+            if (parseNfPacketMessage(nh))
+                return true;
+
         }
     }
 
@@ -588,7 +636,8 @@
 }
 
 bool NetlinkEvent::decode(char *buffer, int size, int format) {
-    if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
+    if (format == NetlinkListener::NETLINK_FORMAT_BINARY
+            || format == NetlinkListener::NETLINK_FORMAT_BINARY_UNICAST) {
         return parseBinaryNetlinkMessage(buffer, size);
     } else {
         return parseAsciiNetlinkMessage(buffer, size);
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index 81c5cc2..637aa1e 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -47,8 +47,13 @@
     ssize_t count;
     uid_t uid = -1;
 
-    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
-                                       socket, mBuffer, sizeof(mBuffer), &uid));
+    bool require_group = true;
+    if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {
+        require_group = false;
+    }
+
+    count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,
+            mBuffer, sizeof(mBuffer), require_group, &uid));
     if (count < 0) {
         if (uid > 0)
             LOG_EVENT_INT(65537, uid);