Merge changes from topic "bpfinterfacemap"
* changes:
Decouple "net-utils-framework-common" from "net-utils-device-common-bpf"
Add visibility for use bpfmap by BpfInterfaceMapUpdater
diff --git a/common/device/com/android/net/module/util/Struct.java b/common/device/com/android/net/module/util/Struct.java
index b43e2c4..f4d4163 100644
--- a/common/device/com/android/net/module/util/Struct.java
+++ b/common/device/com/android/net/module/util/Struct.java
@@ -730,4 +730,22 @@
}
return sb.toString();
}
+
+ public static class U32 extends Struct {
+ @Struct.Field(order = 0, type = Struct.Type.U32)
+ public final long val;
+
+ public U32(final long val) {
+ this.val = val;
+ }
+ }
+
+ public static class S64 extends Struct {
+ @Struct.Field(order = 0, type = Struct.Type.S64)
+ public final long val;
+
+ public S64(final long val) {
+ this.val = val;
+ }
+ }
}
diff --git a/common/native/bpf_headers/Android.bp b/common/native/bpf_headers/Android.bp
index af8ec73..a9f6984 100644
--- a/common/native/bpf_headers/Android.bp
+++ b/common/native/bpf_headers/Android.bp
@@ -52,7 +52,6 @@
"//system/bpf/bpfloader",
"//system/bpf/libbpf_android",
"//system/memory/libmeminfo",
- "//system/netd/libnetdbpf",
"//system/netd/server",
"//system/netd/tests",
"//system/netd/tests/benchmarks",
diff --git a/common/netd/libnetdutils/Android.bp b/common/netd/libnetdutils/Android.bp
index 732e37d..08d5412 100644
--- a/common/netd/libnetdutils/Android.bp
+++ b/common/netd/libnetdutils/Android.bp
@@ -11,6 +11,7 @@
"Log.cpp",
"Netfilter.cpp",
"Netlink.cpp",
+ "NetlinkListener.cpp",
"Slice.cpp",
"Socket.cpp",
"SocketOption.cpp",
diff --git a/common/netd/libnetdutils/NetlinkListener.cpp b/common/netd/libnetdutils/NetlinkListener.cpp
new file mode 100644
index 0000000..decaa9c
--- /dev/null
+++ b/common/netd/libnetdutils/NetlinkListener.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NetlinkListener"
+
+#include <sstream>
+#include <vector>
+
+#include <linux/netfilter/nfnetlink.h>
+
+#include <log/log.h>
+#include <netdutils/Misc.h>
+#include <netdutils/NetlinkListener.h>
+#include <netdutils/Syscalls.h>
+
+namespace android {
+namespace netdutils {
+
+using netdutils::Fd;
+using netdutils::Slice;
+using netdutils::Status;
+using netdutils::UniqueFd;
+using netdutils::findWithDefault;
+using netdutils::forEachNetlinkMessage;
+using netdutils::makeSlice;
+using netdutils::sSyscalls;
+using netdutils::status::ok;
+using netdutils::statusFromErrno;
+
+namespace {
+
+constexpr int kNetlinkMsgErrorType = (NFNL_SUBSYS_NONE << 8) | NLMSG_ERROR;
+
+constexpr sockaddr_nl kKernelAddr = {
+ .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = 0,
+};
+
+const NetlinkListener::DispatchFn kDefaultDispatchFn = [](const nlmsghdr& nlmsg, const Slice) {
+ std::stringstream ss;
+ ss << nlmsg;
+ ALOGE("unhandled netlink message: %s", ss.str().c_str());
+};
+
+} // namespace
+
+NetlinkListener::NetlinkListener(UniqueFd event, UniqueFd sock, const std::string& name)
+ : mEvent(std::move(event)), mSock(std::move(sock)), mThreadName(name) {
+ const auto rxErrorHandler = [](const nlmsghdr& nlmsg, const Slice msg) {
+ std::stringstream ss;
+ ss << nlmsg << " " << msg << " " << netdutils::toHex(msg, 32);
+ ALOGE("unhandled netlink message: %s", ss.str().c_str());
+ };
+ expectOk(NetlinkListener::subscribe(kNetlinkMsgErrorType, rxErrorHandler));
+
+ mErrorHandler = [& name = mThreadName](const int fd, const int err) {
+ ALOGE("Error on NetlinkListener(%s) fd=%d: %s", name.c_str(), fd, strerror(err));
+ };
+
+ // Start the thread
+ mWorker = std::thread([this]() { run().ignoreError(); });
+}
+
+NetlinkListener::~NetlinkListener() {
+ const auto& sys = sSyscalls.get();
+ const uint64_t data = 1;
+ // eventfd should never enter an error state unexpectedly
+ expectOk(sys.write(mEvent, makeSlice(data)).status());
+ mWorker.join();
+}
+
+Status NetlinkListener::send(const Slice msg) {
+ const auto& sys = sSyscalls.get();
+ ASSIGN_OR_RETURN(auto sent, sys.sendto(mSock, msg, 0, kKernelAddr));
+ if (sent != msg.size()) {
+ return statusFromErrno(EMSGSIZE, "unexpect message size");
+ }
+ return ok;
+}
+
+Status NetlinkListener::subscribe(uint16_t type, const DispatchFn& fn) {
+ std::lock_guard guard(mMutex);
+ mDispatchMap[type] = fn;
+ return ok;
+}
+
+Status NetlinkListener::unsubscribe(uint16_t type) {
+ std::lock_guard guard(mMutex);
+ mDispatchMap.erase(type);
+ return ok;
+}
+
+void NetlinkListener::registerSkErrorHandler(const SkErrorHandler& handler) {
+ mErrorHandler = handler;
+}
+
+Status NetlinkListener::run() {
+ std::vector<char> rxbuf(4096);
+
+ const auto rxHandler = [this](const nlmsghdr& nlmsg, const Slice& buf) {
+ std::lock_guard guard(mMutex);
+ const auto& fn = findWithDefault(mDispatchMap, nlmsg.nlmsg_type, kDefaultDispatchFn);
+ fn(nlmsg, buf);
+ };
+
+ if (mThreadName.length() > 0) {
+ int ret = pthread_setname_np(pthread_self(), mThreadName.c_str());
+ if (ret) {
+ ALOGE("thread name set failed, name: %s, ret: %s", mThreadName.c_str(), strerror(ret));
+ }
+ }
+ const auto& sys = sSyscalls.get();
+ const std::array<Fd, 2> fds{{{mEvent}, {mSock}}};
+ const int events = POLLIN;
+ const double timeout = 3600;
+ while (true) {
+ ASSIGN_OR_RETURN(auto revents, sys.ppoll(fds, events, timeout));
+ // After mEvent becomes readable, we should stop servicing mSock and return
+ if (revents[0] & POLLIN) {
+ break;
+ }
+ if (revents[1] & (POLLIN|POLLERR)) {
+ auto rx = sys.recvfrom(mSock, makeSlice(rxbuf), 0);
+ int err = rx.status().code();
+ if (err) {
+ // Ignore errors. The only error we expect to see here is ENOBUFS, and there's
+ // nothing we can do about that. The recvfrom above will already have cleared the
+ // error indication and ensured we won't get EPOLLERR again.
+ // TODO: Consider using NETLINK_NO_ENOBUFS.
+ mErrorHandler(((Fd) mSock).get(), err);
+ continue;
+ }
+ forEachNetlinkMessage(rx.value(), rxHandler);
+ }
+ }
+ return ok;
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/common/netd/libnetdutils/include/netdutils/NetlinkListener.h b/common/netd/libnetdutils/include/netdutils/NetlinkListener.h
new file mode 100644
index 0000000..97f7bb2
--- /dev/null
+++ b/common/netd/libnetdutils/include/netdutils/NetlinkListener.h
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+#ifndef NETLINK_LISTENER_H
+#define NETLINK_LISTENER_H
+
+#include <functional>
+#include <map>
+#include <mutex>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+#include <netdutils/Netlink.h>
+#include <netdutils/Slice.h>
+#include <netdutils/Status.h>
+#include <netdutils/UniqueFd.h>
+
+namespace android {
+namespace netdutils {
+
+class NetlinkListenerInterface {
+ public:
+ using DispatchFn = std::function<void(const nlmsghdr& nlmsg, const netdutils::Slice msg)>;
+
+ using SkErrorHandler = std::function<void(const int fd, const int err)>;
+
+ virtual ~NetlinkListenerInterface() = default;
+
+ // Send message to the kernel using the underlying netlink socket
+ virtual netdutils::Status send(const netdutils::Slice msg) = 0;
+
+ // Deliver future messages with nlmsghdr.nlmsg_type == type to fn.
+ //
+ // Threadsafe.
+ // All dispatch functions invoked on a single service thread.
+ // subscribe() and join() must not be called from the stack of fn().
+ virtual netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) = 0;
+
+ // Halt delivery of future messages with nlmsghdr.nlmsg_type == type.
+ // Threadsafe.
+ virtual netdutils::Status unsubscribe(uint16_t type) = 0;
+
+ virtual void registerSkErrorHandler(const SkErrorHandler& handler) = 0;
+};
+
+// NetlinkListener manages a netlink socket and associated blocking
+// service thread.
+//
+// This class is written in a generic way to allow multiple different
+// netlink subsystems to share this common infrastructure. If multiple
+// subsystems share the same message delivery requirements (drops ok,
+// no drops) they may share a single listener by calling subscribe()
+// with multiple types.
+//
+// This class is suitable for moderate performance message
+// processing. In particular it avoids extra copies of received
+// message data and allows client code to control which message
+// attributes are processed.
+//
+// Note that NetlinkListener is capable of processing multiple batched
+// netlink messages in a single system call. This is useful to
+// netfilter extensions that allow batching of events like NFLOG.
+class NetlinkListener : public NetlinkListenerInterface {
+ public:
+ NetlinkListener(netdutils::UniqueFd event, netdutils::UniqueFd sock, const std::string& name);
+
+ ~NetlinkListener() override;
+
+ netdutils::Status send(const netdutils::Slice msg) override;
+
+ netdutils::Status subscribe(uint16_t type, const DispatchFn& fn) override EXCLUDES(mMutex);
+
+ netdutils::Status unsubscribe(uint16_t type) override EXCLUDES(mMutex);
+
+ void registerSkErrorHandler(const SkErrorHandler& handler) override;
+
+ private:
+ netdutils::Status run();
+
+ const netdutils::UniqueFd mEvent;
+ const netdutils::UniqueFd mSock;
+ const std::string mThreadName;
+ std::mutex mMutex;
+ std::map<uint16_t, DispatchFn> mDispatchMap GUARDED_BY(mMutex);
+ std::thread mWorker;
+ SkErrorHandler mErrorHandler;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETLINK_LISTENER_H */
diff --git a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
index 201bf2d..8b58e71 100644
--- a/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
+++ b/common/testutils/devicetests/com/android/testutils/DevSdkIgnoreRule.kt
@@ -18,11 +18,15 @@
import android.os.Build
import com.android.modules.utils.build.SdkLevel
+import kotlin.test.fail
import org.junit.Assume.assumeTrue
import org.junit.rules.TestRule
import org.junit.runner.Description
import org.junit.runners.model.Statement
+// TODO: Remove it when Build.VERSION_CODES.SC_V2 is available
+const val SC_V2 = 32
+
/**
* Returns true if the development SDK version of the device is in the provided range.
*
@@ -40,8 +44,10 @@
// For recent SDKs that still have development builds used for testing, use SdkLevel utilities
// instead of SDK_INT.
return when (minExclusive) {
- // TODO: use Build.VERSION_CODES.S when it is not CURRENT_DEVELOPMENT
- 31 -> SdkLevel.isAtLeastT()
+ // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
+ SC_V2 -> SdkLevel.isAtLeastT()
+ // TODO: To use SdkLevel.isAtLeastSv2 when available
+ Build.VERSION_CODES.S -> fail("Do you expect to ignore the test until T? Use SC_V2 instead")
Build.VERSION_CODES.R -> SdkLevel.isAtLeastS()
// Development builds of SDK versions <= R are not used anymore
else -> Build.VERSION.SDK_INT > minExclusive
@@ -50,8 +56,11 @@
private fun isDevSdkUpTo(maxInclusive: Int): Boolean {
return when (maxInclusive) {
- // TODO: use Build.VERSION_CODES.S when it is not CURRENT_DEVELOPMENT
- 31 -> !SdkLevel.isAtLeastT()
+ // TODO: Use Build.VERSION_CODES.SC_V2 when it is available
+ SC_V2 -> !SdkLevel.isAtLeastT()
+ // TODO: To use SdkLevel.isAtLeastSv2 when available
+ Build.VERSION_CODES.S ->
+ fail("Do you expect to ignore the test before T? Use SC_V2 instead")
Build.VERSION_CODES.R -> !SdkLevel.isAtLeastS()
// Development builds of SDK versions <= R are not used anymore
else -> Build.VERSION.SDK_INT <= maxInclusive
diff --git a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt b/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
index 40fb773..a96f78b 100644
--- a/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
+++ b/common/testutils/devicetests/com/android/testutils/TestableNetworkAgent.kt
@@ -30,6 +30,7 @@
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAddKeepalivePacketFilter
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnAutomaticReconnectDisabled
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnBandwidthUpdateRequested
+import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnDscpPolicyStatusUpdated
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkCreated
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkDestroyed
import com.android.testutils.TestableNetworkAgent.CallbackEntry.OnNetworkUnwanted
@@ -89,6 +90,7 @@
data class OnSignalStrengthThresholdsUpdated(val thresholds: IntArray) : CallbackEntry()
object OnNetworkCreated : CallbackEntry()
object OnNetworkDestroyed : CallbackEntry()
+ data class OnDscpPolicyStatusUpdated(val policyId: Int, val status: Int) : CallbackEntry()
data class OnRegisterQosCallback(
val callbackId: Int,
val filter: QosFilter
@@ -162,6 +164,10 @@
history.add(OnNetworkDestroyed)
}
+ fun onDscpPolicyStatusUpdated(policyId: Int, status: Int) {
+ history.add(OnDscpPolicyStatusUpdated(policyId, status))
+ }
+
// Expects the initial validation event that always occurs immediately after registering
// a NetworkAgent whose network does not require validation (which test networks do
// not, since they lack the INTERNET capability). It always contains the default argument