Merge "Return null RtNetlinkAddressMessage if IFA_FLAGS attr is malformed."
diff --git a/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java b/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
index 83a82b7..c44a5b4 100644
--- a/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
+++ b/staticlibs/device/com/android/net/module/util/netlink/NetlinkConstants.java
@@ -151,6 +151,7 @@
public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
// Device flags.
+ public static final int IFF_UP = 1 << 0;
public static final int IFF_LOWER_UP = 1 << 16;
// Known values for struct rtmsg rtm_protocol.
diff --git a/staticlibs/device/com/android/net/module/util/structs/Ipv4Header.java b/staticlibs/device/com/android/net/module/util/structs/Ipv4Header.java
new file mode 100644
index 0000000..5249454
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/Ipv4Header.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.net.module.util.structs;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+import java.net.Inet4Address;
+
+/**
+ * L3 IPv4 header as per https://tools.ietf.org/html/rfc791.
+ * This class doesn't contain options field.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| IHL |Type of Service| Total Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Identification |Flags| Fragment Offset |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Time to Live | Protocol | Header Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Destination Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class Ipv4Header extends Struct {
+ // IP Version=IPv4, IHL is always 5(*4bytes) because options are not supported.
+ @VisibleForTesting
+ public static final byte IPHDR_VERSION_IHL = 0x45;
+
+ @Field(order = 0, type = Type.S8)
+ // version (4 bits), IHL (4 bits)
+ public final byte vi;
+ @Field(order = 1, type = Type.S8)
+ public final byte tos;
+ @Field(order = 2, type = Type.U16)
+ public final int totalLength;
+ @Field(order = 3, type = Type.S16)
+ public final short id;
+ @Field(order = 4, type = Type.S16)
+ // flags (3 bits), fragment offset (13 bits)
+ public final short flagsAndFragmentOffset;
+ @Field(order = 5, type = Type.U8)
+ public final short ttl;
+ @Field(order = 6, type = Type.S8)
+ public final byte protocol;
+ @Field(order = 7, type = Type.S16)
+ public final short checksum;
+ @Field(order = 8, type = Type.Ipv4Address)
+ public final Inet4Address srcIp;
+ @Field(order = 9, type = Type.Ipv4Address)
+ public final Inet4Address dstIp;
+
+ public Ipv4Header(final byte tos, final int totalLength, final short id,
+ final short flagsAndFragmentOffset, final short ttl, final byte protocol,
+ final short checksum, final Inet4Address srcIp, final Inet4Address dstIp) {
+ this(IPHDR_VERSION_IHL, tos, totalLength, id, flagsAndFragmentOffset, ttl,
+ protocol, checksum, srcIp, dstIp);
+ }
+
+ private Ipv4Header(final byte vi, final byte tos, final int totalLength, final short id,
+ final short flagsAndFragmentOffset, final short ttl, final byte protocol,
+ final short checksum, final Inet4Address srcIp, final Inet4Address dstIp) {
+ this.vi = vi;
+ this.tos = tos;
+ this.totalLength = totalLength;
+ this.id = id;
+ this.flagsAndFragmentOffset = flagsAndFragmentOffset;
+ this.ttl = ttl;
+ this.protocol = protocol;
+ this.checksum = checksum;
+ this.srcIp = srcIp;
+ this.dstIp = dstIp;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/TcpHeader.java b/staticlibs/device/com/android/net/module/util/structs/TcpHeader.java
new file mode 100644
index 0000000..0c97401
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/TcpHeader.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * L4 TCP header as per https://tools.ietf.org/html/rfc793.
+ * This class does not contain option and data fields.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Port | Destination Port |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Acknowledgment Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Data | |U|A|P|R|S|F| |
+ * | Offset| Reserved |R|C|S|S|Y|I| Window |
+ * | | |G|K|H|T|N|N| |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Checksum | Urgent Pointer |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Options | Padding |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+public class TcpHeader extends Struct {
+ @Field(order = 0, type = Type.U16)
+ public final int srcPort;
+ @Field(order = 1, type = Type.U16)
+ public final int dstPort;
+ @Field(order = 2, type = Type.U32)
+ public final long seq;
+ @Field(order = 3, type = Type.U32)
+ public final long ack;
+ @Field(order = 4, type = Type.S16)
+ // data Offset (4 bits), reserved (6 bits), control bits (6 bits)
+ // TODO: update with bitfields once class Struct supports it
+ public final short dataOffsetAndControlBits;
+ @Field(order = 5, type = Type.U16)
+ public final int window;
+ @Field(order = 6, type = Type.S16)
+ public final short checksum;
+ @Field(order = 7, type = Type.U16)
+ public final int urgentPointer;
+
+ public TcpHeader(final int srcPort, final int dstPort, final long seq, final long ack,
+ final short dataOffsetAndControlBits, final int window, final short checksum,
+ final int urgentPointer) {
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ this.seq = seq;
+ this.ack = ack;
+ this.dataOffsetAndControlBits = dataOffsetAndControlBits;
+ this.window = window;
+ this.checksum = checksum;
+ this.urgentPointer = urgentPointer;
+ }
+}
diff --git a/staticlibs/device/com/android/net/module/util/structs/UdpHeader.java b/staticlibs/device/com/android/net/module/util/structs/UdpHeader.java
new file mode 100644
index 0000000..8b0316b
--- /dev/null
+++ b/staticlibs/device/com/android/net/module/util/structs/UdpHeader.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+package com.android.net.module.util.structs;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * L4 UDP header as per https://tools.ietf.org/html/rfc768.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Port | Destination Port |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Length | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | data octets ...
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ...
+ */
+public class UdpHeader extends Struct {
+ @Field(order = 0, type = Type.U16)
+ public final int srcPort;
+ @Field(order = 1, type = Type.U16)
+ public final int dstPort;
+ @Field(order = 2, type = Type.U16)
+ public final int length;
+ @Field(order = 3, type = Type.S16)
+ public final short checksum;
+
+ public UdpHeader(final int srcPort, final int dstPort, final int length,
+ final short checksum) {
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ this.length = length;
+ this.checksum = checksum;
+ }
+}
diff --git a/staticlibs/framework/com/android/net/module/util/DnsPacket.java b/staticlibs/framework/com/android/net/module/util/DnsPacket.java
index 4c88000..080781c 100644
--- a/staticlibs/framework/com/android/net/module/util/DnsPacket.java
+++ b/staticlibs/framework/com/android/net/module/util/DnsPacket.java
@@ -131,7 +131,8 @@
*/
DnsRecord(int recordType, @NonNull ByteBuffer buf)
throws BufferUnderflowException, ParseException {
- dName = DnsRecordParser.parseName(buf, 0 /* Parse depth */);
+ dName = DnsRecordParser.parseName(buf, 0 /* Parse depth */,
+ /* isNameCompressionSupported= */ true);
if (dName.length() > MAXNAMESIZE) {
throw new ParseException(
"Parse name fail, name size is too long: " + dName.length());
diff --git a/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java b/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java
index 677474c..3448cad 100644
--- a/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java
+++ b/staticlibs/framework/com/android/net/module/util/DnsPacketUtils.java
@@ -73,8 +73,14 @@
/**
* Parses the domain / target name of a DNS record.
+ *
+ * As described in RFC 1035 Section 4.1.3, the NAME field of a DNS Resource Record always
+ * supports Name Compression, whereas domain names contained in the RDATA payload of a DNS
+ * record may or may not support Name Compression, depending on the record TYPE. Moreover,
+ * even if Name Compression is supported, its usage is left to the implementation.
*/
- public static String parseName(ByteBuffer buf, int depth) throws
+ public static String parseName(ByteBuffer buf, int depth,
+ boolean isNameCompressionSupported) throws
BufferUnderflowException, DnsPacket.ParseException {
if (depth > MAXLABELCOUNT) {
throw new DnsPacket.ParseException("Failed to parse name, too many labels");
@@ -83,7 +89,8 @@
final int mask = len & NAME_COMPRESSION;
if (0 == len) {
return "";
- } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION) {
+ } else if (mask != NAME_NORMAL && mask != NAME_COMPRESSION
+ || (!isNameCompressionSupported && mask == NAME_COMPRESSION)) {
throw new DnsPacket.ParseException("Parse name fail, bad label type: " + mask);
} else if (mask == NAME_COMPRESSION) {
// Name compression based on RFC 1035 - 4.1.4 Message compression
@@ -94,7 +101,7 @@
"Parse compression name fail, invalid compression");
}
buf.position(offset);
- final String pointed = parseName(buf, depth + 1);
+ final String pointed = parseName(buf, depth + 1, isNameCompressionSupported);
buf.position(oldPos);
return pointed;
} else {
@@ -104,7 +111,7 @@
if (head.length() > MAXLABELSIZE) {
throw new DnsPacket.ParseException("Parse name fail, invalid label length");
}
- final String tail = parseName(buf, depth + 1);
+ final String tail = parseName(buf, depth + 1, isNameCompressionSupported);
return TextUtils.isEmpty(tail) ? head : head + "." + tail;
}
}
diff --git a/staticlibs/native/bpf_map_utils/Android.bp b/staticlibs/native/bpf_map_utils/Android.bp
new file mode 100644
index 0000000..e14c259
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/Android.bp
@@ -0,0 +1,79 @@
+// Copyright (C) 2021 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.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library_headers {
+ name: "bpf_map_utils",
+ vendor_available: true,
+ host_supported: true,
+ native_bridge_supported: true,
+ export_include_dirs: ["include"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.tethering",
+ "com.android.art.debug",
+ ],
+ visibility: [
+ "//bootable/libbootloader/vts",
+ "//frameworks/base/services/core/jni",
+ "//frameworks/native/libs/cputimeinstate",
+ "//frameworks/native/services/gpuservice",
+ "//frameworks/native/services/gpuservice/gpumem",
+ "//frameworks/native/services/gpuservice/tests/unittests",
+ "//frameworks/native/services/gpuservice/tracing",
+ "//packages/modules/Connectivity/netd",
+ "//packages/modules/Connectivity/tests/unit/jni",
+ "//packages/modules/DnsResolver/tests",
+ "//system/bpf/bpfloader",
+ "//system/bpf/libbpf_android",
+ "//system/memory/libmeminfo",
+ "//system/netd/libnetdbpf",
+ "//system/netd/server",
+ "//system/netd/tests",
+ "//system/netd/tests/benchmarks",
+ "//test/vts-testcase/kernel/api/bpf_native_test",
+ ],
+}
+
+
+cc_test {
+ // TODO: Rename to bpf_map_test and modify .gcls as well.
+ name: "libbpf_android_test",
+ srcs: [
+ "BpfMapTest.cpp",
+ ],
+ defaults: ["bpf_defaults"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-error=unused-variable",
+ ],
+ static_libs: ["libgmock"],
+ shared_libs: [
+ "libbpf_android",
+ "libbase",
+ "liblog",
+ "libutils",
+ ],
+ require_root: true,
+ test_suites: ["general-tests"],
+}
diff --git a/staticlibs/native/bpf_map_utils/BpfMapTest.cpp b/staticlibs/native/bpf_map_utils/BpfMapTest.cpp
new file mode 100644
index 0000000..d0737b0
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/BpfMapTest.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2018 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 <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <fcntl.h>
+#include <inttypes.h>
+#include <linux/inet_diag.h>
+#include <linux/sock_diag.h>
+#include <net/if.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include "bpf/BpfMap.h"
+#include "bpf/BpfUtils.h"
+
+using ::testing::Test;
+
+namespace android {
+namespace bpf {
+
+using base::Result;
+using base::unique_fd;
+
+constexpr uint32_t TEST_MAP_SIZE = 10;
+constexpr uint32_t TEST_KEY1 = 1;
+constexpr uint32_t TEST_VALUE1 = 10;
+constexpr const char PINNED_MAP_PATH[] = "/sys/fs/bpf/testMap";
+
+class BpfMapTest : public testing::Test {
+ protected:
+ BpfMapTest() {}
+
+ void SetUp() {
+ EXPECT_EQ(0, setrlimitForTest());
+ if (!access(PINNED_MAP_PATH, R_OK)) {
+ EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+ }
+ }
+
+ void TearDown() {
+ if (!access(PINNED_MAP_PATH, R_OK)) {
+ EXPECT_EQ(0, remove(PINNED_MAP_PATH));
+ }
+ }
+
+ void checkMapInvalid(BpfMap<uint32_t, uint32_t>& map) {
+ EXPECT_FALSE(map.isValid());
+ EXPECT_EQ(-1, map.getMap().get());
+ }
+
+ void checkMapValid(BpfMap<uint32_t, uint32_t>& map) {
+ EXPECT_LE(0, map.getMap().get());
+ EXPECT_TRUE(map.isValid());
+ }
+
+ void writeToMapAndCheck(BpfMap<uint32_t, uint32_t>& map, uint32_t key, uint32_t value) {
+ ASSERT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
+ uint32_t value_read;
+ ASSERT_EQ(0, findMapEntry(map.getMap(), &key, &value_read));
+ checkValueAndStatus(value, value_read);
+ }
+
+ void checkValueAndStatus(uint32_t refValue, Result<uint32_t> value) {
+ ASSERT_RESULT_OK(value);
+ ASSERT_EQ(refValue, value.value());
+ }
+
+ void populateMap(uint32_t total, BpfMap<uint32_t, uint32_t>& map) {
+ for (uint32_t key = 0; key < total; key++) {
+ uint32_t value = key * 10;
+ EXPECT_RESULT_OK(map.writeValue(key, value, BPF_ANY));
+ }
+ }
+
+ void expectMapEmpty(BpfMap<uint32_t, uint32_t>& map) {
+ Result<bool> isEmpty = map.isEmpty();
+ ASSERT_RESULT_OK(isEmpty);
+ ASSERT_TRUE(isEmpty.value());
+ }
+};
+
+TEST_F(BpfMapTest, constructor) {
+ BpfMap<uint32_t, uint32_t> testMap1;
+ checkMapInvalid(testMap1);
+
+ BpfMap<uint32_t, uint32_t> testMap2(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ checkMapValid(testMap2);
+}
+
+TEST_F(BpfMapTest, basicHelpers) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ uint32_t key = TEST_KEY1;
+ uint32_t value_write = TEST_VALUE1;
+ writeToMapAndCheck(testMap, key, value_write);
+ Result<uint32_t> value_read = testMap.readValue(key);
+ checkValueAndStatus(value_write, value_read);
+ Result<uint32_t> key_read = testMap.getFirstKey();
+ checkValueAndStatus(key, key_read);
+ ASSERT_RESULT_OK(testMap.deleteValue(key));
+ ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_read));
+ ASSERT_EQ(ENOENT, errno);
+}
+
+TEST_F(BpfMapTest, reset) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ uint32_t key = TEST_KEY1;
+ uint32_t value_write = TEST_VALUE1;
+ writeToMapAndCheck(testMap, key, value_write);
+
+ testMap.reset(-1);
+ checkMapInvalid(testMap);
+ ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
+ ASSERT_EQ(EBADF, errno);
+}
+
+TEST_F(BpfMapTest, moveConstructor) {
+ BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ BpfMap<uint32_t, uint32_t> testMap2;
+ testMap2 = std::move(testMap1);
+ uint32_t key = TEST_KEY1;
+ checkMapInvalid(testMap1);
+ uint32_t value = TEST_VALUE1;
+ writeToMapAndCheck(testMap2, key, value);
+}
+
+TEST_F(BpfMapTest, SetUpMap) {
+ EXPECT_NE(0, access(PINNED_MAP_PATH, R_OK));
+ BpfMap<uint32_t, uint32_t> testMap1(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ ASSERT_EQ(0, bpfFdPin(testMap1.getMap(), PINNED_MAP_PATH));
+ EXPECT_EQ(0, access(PINNED_MAP_PATH, R_OK));
+ checkMapValid(testMap1);
+ BpfMap<uint32_t, uint32_t> testMap2;
+ EXPECT_RESULT_OK(testMap2.init(PINNED_MAP_PATH));
+ checkMapValid(testMap2);
+ uint32_t key = TEST_KEY1;
+ uint32_t value = TEST_VALUE1;
+ writeToMapAndCheck(testMap1, key, value);
+ Result<uint32_t> value_read = testMap2.readValue(key);
+ checkValueAndStatus(value, value_read);
+}
+
+TEST_F(BpfMapTest, iterate) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ populateMap(TEST_MAP_SIZE, testMap);
+ int totalCount = 0;
+ int totalSum = 0;
+ const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
+ BpfMap<uint32_t, uint32_t>& map) {
+ EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
+ totalCount++;
+ totalSum += key;
+ return map.deleteValue(key);
+ };
+ EXPECT_RESULT_OK(testMap.iterate(iterateWithDeletion));
+ EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
+ EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) / 2, (uint32_t)totalSum);
+ expectMapEmpty(testMap);
+}
+
+TEST_F(BpfMapTest, iterateWithValue) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ populateMap(TEST_MAP_SIZE, testMap);
+ int totalCount = 0;
+ int totalSum = 0;
+ const auto iterateWithDeletion = [&totalCount, &totalSum](const uint32_t& key,
+ const uint32_t& value,
+ BpfMap<uint32_t, uint32_t>& map) {
+ EXPECT_GE((uint32_t)TEST_MAP_SIZE, key);
+ EXPECT_EQ(value, key * 10);
+ totalCount++;
+ totalSum += value;
+ return map.deleteValue(key);
+ };
+ EXPECT_RESULT_OK(testMap.iterateWithValue(iterateWithDeletion));
+ EXPECT_EQ((int)TEST_MAP_SIZE, totalCount);
+ EXPECT_EQ(((1 + TEST_MAP_SIZE - 1) * (TEST_MAP_SIZE - 1)) * 5, (uint32_t)totalSum);
+ expectMapEmpty(testMap);
+}
+
+TEST_F(BpfMapTest, mapIsEmpty) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ expectMapEmpty(testMap);
+ uint32_t key = TEST_KEY1;
+ uint32_t value_write = TEST_VALUE1;
+ writeToMapAndCheck(testMap, key, value_write);
+ Result<bool> isEmpty = testMap.isEmpty();
+ ASSERT_RESULT_OK(isEmpty);
+ ASSERT_FALSE(isEmpty.value());
+ ASSERT_RESULT_OK(testMap.deleteValue(key));
+ ASSERT_GT(0, findMapEntry(testMap.getMap(), &key, &value_write));
+ ASSERT_EQ(ENOENT, errno);
+ expectMapEmpty(testMap);
+ int entriesSeen = 0;
+ EXPECT_RESULT_OK(testMap.iterate(
+ [&entriesSeen](const unsigned int&,
+ const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
+ entriesSeen++;
+ return {};
+ }));
+ EXPECT_EQ(0, entriesSeen);
+ EXPECT_RESULT_OK(testMap.iterateWithValue(
+ [&entriesSeen](const unsigned int&, const unsigned int&,
+ const BpfMap<unsigned int, unsigned int>&) -> Result<void> {
+ entriesSeen++;
+ return {};
+ }));
+ EXPECT_EQ(0, entriesSeen);
+}
+
+TEST_F(BpfMapTest, mapClear) {
+ BpfMap<uint32_t, uint32_t> testMap(BPF_MAP_TYPE_HASH, TEST_MAP_SIZE, BPF_F_NO_PREALLOC);
+ populateMap(TEST_MAP_SIZE, testMap);
+ Result<bool> isEmpty = testMap.isEmpty();
+ ASSERT_RESULT_OK(isEmpty);
+ ASSERT_FALSE(*isEmpty);
+ ASSERT_RESULT_OK(testMap.clear());
+ expectMapEmpty(testMap);
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/staticlibs/native/bpf_map_utils/TEST_MAPPING b/staticlibs/native/bpf_map_utils/TEST_MAPPING
new file mode 100644
index 0000000..9ec8a40
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libbpf_android_test"
+ }
+ ]
+}
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h b/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h
new file mode 100644
index 0000000..bdffc0f
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/BpfMap.h
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <linux/bpf.h>
+
+#include <android-base/result.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <utils/Log.h>
+#include "bpf/BpfUtils.h"
+
+namespace android {
+namespace bpf {
+
+// This is a class wrapper for eBPF maps. The eBPF map is a special in-kernel
+// data structure that stores data in <Key, Value> pairs. It can be read/write
+// from userspace by passing syscalls with the map file descriptor. This class
+// is used to generalize the procedure of interacting with eBPF maps and hide
+// the implementation detail from other process. Besides the basic syscalls
+// wrapper, it also provides some useful helper functions as well as an iterator
+// nested class to iterate the map more easily.
+//
+// NOTE: A kernel eBPF map may be accessed by both kernel and userspace
+// processes at the same time. Or if the map is pinned as a virtual file, it can
+// be obtained by multiple eBPF map class object and accessed concurrently.
+// Though the map class object and the underlying kernel map are thread safe, it
+// is not safe to iterate over a map while another thread or process is deleting
+// from it. In this case the iteration can return duplicate entries.
+template <class Key, class Value>
+class BpfMap {
+ public:
+ BpfMap<Key, Value>() {};
+
+ protected:
+ // flag must be within BPF_OBJ_FLAG_MASK, ie. 0, BPF_F_RDONLY, BPF_F_WRONLY
+ BpfMap<Key, Value>(const char* pathname, uint32_t flags) {
+ int map_fd = mapRetrieve(pathname, flags);
+ if (map_fd >= 0) mMapFd.reset(map_fd);
+ }
+
+ public:
+ explicit BpfMap<Key, Value>(const char* pathname) : BpfMap<Key, Value>(pathname, 0) {}
+
+ BpfMap<Key, Value>(bpf_map_type map_type, uint32_t max_entries, uint32_t map_flags = 0) {
+ int map_fd = createMap(map_type, sizeof(Key), sizeof(Value), max_entries, map_flags);
+ if (map_fd >= 0) mMapFd.reset(map_fd);
+ }
+
+ base::Result<Key> getFirstKey() const {
+ Key firstKey;
+ if (getFirstMapKey(mMapFd, &firstKey)) {
+ return ErrnoErrorf("Get firstKey map {} failed", mMapFd.get());
+ }
+ return firstKey;
+ }
+
+ base::Result<Key> getNextKey(const Key& key) const {
+ Key nextKey;
+ if (getNextMapKey(mMapFd, &key, &nextKey)) {
+ return ErrnoErrorf("Get next key of map {} failed", mMapFd.get());
+ }
+ return nextKey;
+ }
+
+ base::Result<void> writeValue(const Key& key, const Value& value, uint64_t flags) {
+ if (writeToMapEntry(mMapFd, &key, &value, flags)) {
+ return ErrnoErrorf("Write to map {} failed", mMapFd.get());
+ }
+ return {};
+ }
+
+ base::Result<Value> readValue(const Key key) const {
+ Value value;
+ if (findMapEntry(mMapFd, &key, &value)) {
+ return ErrnoErrorf("Read value of map {} failed", mMapFd.get());
+ }
+ return value;
+ }
+
+ base::Result<void> deleteValue(const Key& key) {
+ if (deleteMapEntry(mMapFd, &key)) {
+ return ErrnoErrorf("Delete entry from map {} failed", mMapFd.get());
+ }
+ return {};
+ }
+
+ // Function that tries to get map from a pinned path.
+ base::Result<void> init(const char* path);
+
+ // Iterate through the map and handle each key retrieved based on the filter
+ // without modification of map content.
+ base::Result<void> iterate(
+ const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
+ filter) const;
+
+ // Iterate through the map and get each <key, value> pair, handle each <key,
+ // value> pair based on the filter without modification of map content.
+ base::Result<void> iterateWithValue(
+ const std::function<base::Result<void>(const Key& key, const Value& value,
+ const BpfMap<Key, Value>& map)>& filter) const;
+
+ // Iterate through the map and handle each key retrieved based on the filter
+ base::Result<void> iterate(
+ const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>&
+ filter);
+
+ // Iterate through the map and get each <key, value> pair, handle each <key,
+ // value> pair based on the filter.
+ base::Result<void> iterateWithValue(
+ const std::function<base::Result<void>(const Key& key, const Value& value,
+ BpfMap<Key, Value>& map)>& filter);
+
+ const base::unique_fd& getMap() const { return mMapFd; };
+
+ // Copy assignment operator
+ BpfMap<Key, Value>& operator=(const BpfMap<Key, Value>& other) {
+ if (this != &other) mMapFd.reset(fcntl(other.mMapFd.get(), F_DUPFD_CLOEXEC, 0));
+ return *this;
+ }
+
+ // Move assignment operator
+ BpfMap<Key, Value>& operator=(BpfMap<Key, Value>&& other) noexcept {
+ mMapFd = std::move(other.mMapFd);
+ other.reset(-1);
+ return *this;
+ }
+
+ void reset(base::unique_fd fd) = delete;
+
+ void reset(int fd) { mMapFd.reset(fd); }
+
+ bool isValid() const { return mMapFd != -1; }
+
+ base::Result<void> clear() {
+ while (true) {
+ auto key = getFirstKey();
+ if (!key.ok()) {
+ if (key.error().code() == ENOENT) return {}; // empty: success
+ return key.error(); // Anything else is an error
+ }
+ auto res = deleteValue(key.value());
+ if (!res.ok()) {
+ // Someone else could have deleted the key, so ignore ENOENT
+ if (res.error().code() == ENOENT) continue;
+ ALOGE("Failed to delete data %s", strerror(res.error().code()));
+ return res.error();
+ }
+ }
+ }
+
+ base::Result<bool> isEmpty() const {
+ auto key = getFirstKey();
+ if (!key.ok()) {
+ // Return error code ENOENT means the map is empty
+ if (key.error().code() == ENOENT) return true;
+ return key.error();
+ }
+ return false;
+ }
+
+ private:
+ base::unique_fd mMapFd;
+};
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::init(const char* path) {
+ mMapFd = base::unique_fd(mapRetrieveRW(path));
+ if (mMapFd == -1) {
+ return ErrnoErrorf("Pinned map not accessible or does not exist: ({})", path);
+ }
+ return {};
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterate(
+ const std::function<base::Result<void>(const Key& key, const BpfMap<Key, Value>& map)>&
+ filter) const {
+ base::Result<Key> curKey = getFirstKey();
+ while (curKey.ok()) {
+ const base::Result<Key>& nextKey = getNextKey(curKey.value());
+ base::Result<void> status = filter(curKey.value(), *this);
+ if (!status.ok()) return status;
+ curKey = nextKey;
+ }
+ if (curKey.error().code() == ENOENT) return {};
+ return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterateWithValue(
+ const std::function<base::Result<void>(const Key& key, const Value& value,
+ const BpfMap<Key, Value>& map)>& filter) const {
+ base::Result<Key> curKey = getFirstKey();
+ while (curKey.ok()) {
+ const base::Result<Key>& nextKey = getNextKey(curKey.value());
+ base::Result<Value> curValue = readValue(curKey.value());
+ if (!curValue.ok()) return curValue.error();
+ base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
+ if (!status.ok()) return status;
+ curKey = nextKey;
+ }
+ if (curKey.error().code() == ENOENT) return {};
+ return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterate(
+ const std::function<base::Result<void>(const Key& key, BpfMap<Key, Value>& map)>& filter) {
+ base::Result<Key> curKey = getFirstKey();
+ while (curKey.ok()) {
+ const base::Result<Key>& nextKey = getNextKey(curKey.value());
+ base::Result<void> status = filter(curKey.value(), *this);
+ if (!status.ok()) return status;
+ curKey = nextKey;
+ }
+ if (curKey.error().code() == ENOENT) return {};
+ return curKey.error();
+}
+
+template <class Key, class Value>
+base::Result<void> BpfMap<Key, Value>::iterateWithValue(
+ const std::function<base::Result<void>(const Key& key, const Value& value,
+ BpfMap<Key, Value>& map)>& filter) {
+ base::Result<Key> curKey = getFirstKey();
+ while (curKey.ok()) {
+ const base::Result<Key>& nextKey = getNextKey(curKey.value());
+ base::Result<Value> curValue = readValue(curKey.value());
+ if (!curValue.ok()) return curValue.error();
+ base::Result<void> status = filter(curKey.value(), curValue.value(), *this);
+ if (!status.ok()) return status;
+ curKey = nextKey;
+ }
+ if (curKey.error().code() == ENOENT) return {};
+ return curKey.error();
+}
+
+template <class Key, class Value>
+class BpfMapRO : public BpfMap<Key, Value> {
+ public:
+ explicit BpfMapRO<Key, Value>(const char* pathname)
+ : BpfMap<Key, Value>(pathname, BPF_F_RDONLY) {}
+};
+
+} // namespace bpf
+} // namespace android
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h b/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h
new file mode 100644
index 0000000..265d4b6
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/BpfUtils.h
@@ -0,0 +1,150 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/if_ether.h>
+#include <linux/pfkeyv2.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/utsname.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+#include <log/log.h>
+
+#include "BpfSyscallWrappers.h"
+
+// The buffer size for the buffer that records program loading logs, needs to be large enough for
+// the largest kernel program.
+
+namespace android {
+namespace bpf {
+
+constexpr const int OVERFLOW_COUNTERSET = 2;
+
+constexpr const uint64_t NONEXISTENT_COOKIE = 0;
+
+static inline uint64_t getSocketCookie(int sockFd) {
+ uint64_t sock_cookie;
+ socklen_t cookie_len = sizeof(sock_cookie);
+ int res = getsockopt(sockFd, SOL_SOCKET, SO_COOKIE, &sock_cookie, &cookie_len);
+ if (res < 0) {
+ res = -errno;
+ ALOGE("Failed to get socket cookie: %s\n", strerror(errno));
+ errno = -res;
+ // 0 is an invalid cookie. See sock_gen_cookie.
+ return NONEXISTENT_COOKIE;
+ }
+ return sock_cookie;
+}
+
+static inline int synchronizeKernelRCU() {
+ // This is a temporary hack for network stats map swap on devices running
+ // 4.9 kernels. The kernel code of socket release on pf_key socket will
+ // explicitly call synchronize_rcu() which is exactly what we need.
+ int pfSocket = socket(AF_KEY, SOCK_RAW | SOCK_CLOEXEC, PF_KEY_V2);
+
+ if (pfSocket < 0) {
+ int ret = -errno;
+ ALOGE("create PF_KEY socket failed: %s", strerror(errno));
+ return ret;
+ }
+
+ // When closing socket, synchronize_rcu() gets called in sock_release().
+ if (close(pfSocket)) {
+ int ret = -errno;
+ ALOGE("failed to close the PF_KEY socket: %s", strerror(errno));
+ return ret;
+ }
+ return 0;
+}
+
+static inline int setrlimitForTest() {
+ // Set the memory rlimit for the test process if the default MEMLOCK rlimit is not enough.
+ struct rlimit limit = {
+ .rlim_cur = 1073741824, // 1 GiB
+ .rlim_max = 1073741824, // 1 GiB
+ };
+ int res = setrlimit(RLIMIT_MEMLOCK, &limit);
+ if (res) {
+ ALOGE("Failed to set the default MEMLOCK rlimit: %s", strerror(errno));
+ }
+ return res;
+}
+
+#define KVER(a, b, c) (((a) << 24) + ((b) << 16) + (c))
+
+static inline unsigned kernelVersion() {
+ struct utsname buf;
+ int ret = uname(&buf);
+ if (ret) return 0;
+
+ unsigned kver_major;
+ unsigned kver_minor;
+ unsigned kver_sub;
+ char unused;
+ ret = sscanf(buf.release, "%u.%u.%u%c", &kver_major, &kver_minor, &kver_sub, &unused);
+ // Check the device kernel version
+ if (ret < 3) return 0;
+
+ return KVER(kver_major, kver_minor, kver_sub);
+}
+
+static inline bool isAtLeastKernelVersion(unsigned major, unsigned minor, unsigned sub) {
+ return kernelVersion() >= KVER(major, minor, sub);
+}
+
+#define SKIP_IF_BPF_SUPPORTED \
+ do { \
+ if (android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \
+ GTEST_LOG_(INFO) << "This test is skipped since bpf is supported\n"; \
+ return; \
+ } \
+ } while (0)
+
+#define SKIP_IF_BPF_NOT_SUPPORTED \
+ do { \
+ if (!android::bpf::isAtLeastKernelVersion(4, 9, 0)) { \
+ GTEST_LOG_(INFO) << "This test is skipped since bpf is not supported\n"; \
+ return; \
+ } \
+ } while (0)
+
+#define SKIP_IF_EXTENDED_BPF_NOT_SUPPORTED \
+ do { \
+ if (!android::bpf::isAtLeastKernelVersion(4, 14, 0)) { \
+ GTEST_LOG_(INFO) << "This test is skipped since extended bpf feature" \
+ << "not supported\n"; \
+ return; \
+ } \
+ } while (0)
+
+#define SKIP_IF_XDP_NOT_SUPPORTED \
+ do { \
+ if (!android::bpf::isAtLeastKernelVersion(5, 9, 0)) { \
+ GTEST_LOG_(INFO) << "This test is skipped since xdp" \
+ << "not supported\n"; \
+ return; \
+ } \
+ } while (0)
+
+} // namespace bpf
+} // namespace android
diff --git a/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h b/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h
new file mode 100644
index 0000000..bc4168e
--- /dev/null
+++ b/staticlibs/native/bpf_map_utils/include/bpf/WaitForProgsLoaded.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ * Android BPF library - public API
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <log/log.h>
+
+#include <android-base/properties.h>
+
+namespace android {
+namespace bpf {
+
+// Wait for bpfloader to load BPF programs.
+static inline void waitForProgsLoaded() {
+ // infinite loop until success with 5/10/20/40/60/60/60... delay
+ for (int delay = 5;; delay *= 2) {
+ if (delay > 60) delay = 60;
+ if (android::base::WaitForProperty("bpf.progs_loaded", "1", std::chrono::seconds(delay)))
+ return;
+ ALOGW("Waited %ds for bpf.progs_loaded, still waiting...", delay);
+ }
+}
+
+} // namespace bpf
+} // namespace android
diff --git a/staticlibs/native/bpf_syscall_wrappers/Android.bp b/staticlibs/native/bpf_syscall_wrappers/Android.bp
index 1416b6b..037e10d 100644
--- a/staticlibs/native/bpf_syscall_wrappers/Android.bp
+++ b/staticlibs/native/bpf_syscall_wrappers/Android.bp
@@ -30,12 +30,15 @@
min_sdk_version: "30",
apex_available: [
"//apex_available:platform",
+ "com.android.mediaprovider",
"com.android.tethering",
],
visibility: [
"//frameworks/libs/net/common/native/bpfmapjni",
+ "//packages/modules/Connectivity/netd",
"//packages/modules/Connectivity/service",
"//packages/modules/Connectivity/Tethering",
+ "//packages/providers/MediaProvider/jni",
"//system/bpf/libbpf_android",
"//system/memory/lmkd",
],
diff --git a/staticlibs/netd/libnetdutils/Android.bp b/staticlibs/netd/libnetdutils/Android.bp
new file mode 100644
index 0000000..732e37d
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Android.bp
@@ -0,0 +1,67 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_library {
+ name: "libnetdutils",
+ srcs: [
+ "DumpWriter.cpp",
+ "Fd.cpp",
+ "InternetAddresses.cpp",
+ "Log.cpp",
+ "Netfilter.cpp",
+ "Netlink.cpp",
+ "Slice.cpp",
+ "Socket.cpp",
+ "SocketOption.cpp",
+ "Status.cpp",
+ "Syscalls.cpp",
+ "UniqueFd.cpp",
+ "UniqueFile.cpp",
+ "Utils.cpp",
+ ],
+ defaults: ["netd_defaults"],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ ],
+ export_shared_lib_headers: [
+ "libbase",
+ ],
+ export_include_dirs: ["include"],
+ sanitize: {
+ cfi: true,
+ },
+
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.resolv",
+ "com.android.tethering",
+ ],
+ min_sdk_version: "29",
+}
+
+cc_test {
+ name: "netdutils_test",
+ srcs: [
+ "BackoffSequenceTest.cpp",
+ "FdTest.cpp",
+ "InternetAddressesTest.cpp",
+ "LogTest.cpp",
+ "MemBlockTest.cpp",
+ "SliceTest.cpp",
+ "StatusTest.cpp",
+ "SyscallsTest.cpp",
+ "ThreadUtilTest.cpp",
+ ],
+ defaults: ["netd_defaults"],
+ test_suites: ["device-tests"],
+ static_libs: [
+ "libgmock",
+ "libnetdutils",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/staticlibs/netd/libnetdutils/BackoffSequenceTest.cpp b/staticlibs/netd/libnetdutils/BackoffSequenceTest.cpp
new file mode 100644
index 0000000..b6653fe
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/BackoffSequenceTest.cpp
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2018 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 <gtest/gtest.h>
+
+#include "netdutils/BackoffSequence.h"
+
+namespace android {
+namespace netdutils {
+
+TEST(BackoffSequence, defaults) {
+ BackoffSequence<uint32_t> backoff;
+
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(0x00000001U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000002U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000004U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000008U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000010U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000020U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000040U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000080U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000100U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000200U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000400U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000800U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00001000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00002000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00004000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00008000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00010000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00020000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00040000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00080000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00100000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00200000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00400000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00800000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x01000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x02000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x04000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x08000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x10000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x20000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x40000000U, backoff.getNextTimeout());
+ EXPECT_EQ(0x80000000U, backoff.getNextTimeout());
+ // Maxes out, and stays there, ad infinitum.
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(0xffffffffU, backoff.getNextTimeout());
+ }
+}
+
+TEST(BackoffSequence, backoffToOncePerHour) {
+ auto backoff = BackoffSequence<uint32_t>::Builder()
+ .withInitialRetransmissionTime(1)
+ .withMaximumRetransmissionTime(3600)
+ .build();
+
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(0x00000001U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000002U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000004U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000008U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000010U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000020U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000040U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000080U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000100U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000200U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000400U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000800U, backoff.getNextTimeout());
+ // Maxes out, and stays there, ad infinitum.
+ for (int i = 0; i < 10; i++) {
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(3600U, backoff.getNextTimeout());
+ }
+}
+
+TEST(BackoffSequence, simpleMaxRetransCount) {
+ auto backoff = BackoffSequence<uint32_t>::Builder()
+ .withInitialRetransmissionTime(3)
+ .withMaximumRetransmissionCount(7)
+ .build();
+
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(0x00000003U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000006U, backoff.getNextTimeout());
+ EXPECT_EQ(0x0000000cU, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000018U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000030U, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000060U, backoff.getNextTimeout());
+ EXPECT_EQ(0x000000c0U, backoff.getNextTimeout());
+
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ }
+}
+
+TEST(BackoffSequence, simpleMaxDuration) {
+ auto backoff = BackoffSequence<int>::Builder()
+ .withInitialRetransmissionTime(3)
+ .withMaximumRetransmissionDuration(7)
+ .withEndOfSequenceIndicator(-1)
+ .build();
+
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(0x00000003, backoff.getNextTimeout());
+ EXPECT_EQ(0x00000004, backoff.getNextTimeout());
+
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ EXPECT_EQ(-1, backoff.getNextTimeout());
+ }
+}
+
+TEST(PathologicalBackoffSequence, ZeroInitialRetransTime) {
+ auto backoff = BackoffSequence<std::chrono::seconds>::Builder()
+ .withInitialRetransmissionTime(std::chrono::seconds(0))
+ .build();
+
+ for (int i = 0; i < 10; i++) {
+ // TODO: Decide whether this needs fixing, and how.
+ EXPECT_TRUE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ }
+}
+
+TEST(PathologicalBackoffSequence, MaxRetransDurationGreaterThanInitialRetransTime) {
+ auto backoff = BackoffSequence<std::chrono::milliseconds>::Builder()
+ .withInitialRetransmissionTime(std::chrono::milliseconds(5))
+ .withMaximumRetransmissionDuration(std::chrono::milliseconds(3))
+ .build();
+
+ EXPECT_EQ(std::chrono::milliseconds(3), backoff.getNextTimeout());
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ }
+}
+
+TEST(PathologicalBackoffSequence, MaxRetransDurationEqualsInitialRetransTime) {
+ auto backoff = BackoffSequence<std::chrono::hours>::Builder()
+ .withInitialRetransmissionTime(std::chrono::hours(5))
+ .withMaximumRetransmissionDuration(std::chrono::hours(5))
+ .build();
+
+ EXPECT_EQ(std::chrono::hours(5), backoff.getNextTimeout());
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ }
+}
+
+TEST(PathologicalBackoffSequence, MaxRetransTimeAndDurationGreaterThanInitialRetransTime) {
+ auto backoff = BackoffSequence<std::chrono::nanoseconds>::Builder()
+ .withInitialRetransmissionTime(std::chrono::nanoseconds(7))
+ .withMaximumRetransmissionTime(std::chrono::nanoseconds(3))
+ .withMaximumRetransmissionDuration(std::chrono::nanoseconds(5))
+ .build();
+
+ EXPECT_EQ(std::chrono::nanoseconds(3), backoff.getNextTimeout());
+ EXPECT_EQ(std::chrono::nanoseconds(2), backoff.getNextTimeout());
+ for (int i = 0; i < 10; i++) {
+ EXPECT_FALSE(backoff.hasNextTimeout());
+ EXPECT_EQ(backoff.getEndOfSequenceIndicator(), backoff.getNextTimeout());
+ }
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/DumpWriter.cpp b/staticlibs/netd/libnetdutils/DumpWriter.cpp
new file mode 100644
index 0000000..092ddba
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/DumpWriter.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 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 "netdutils/DumpWriter.h"
+
+#include <unistd.h>
+#include <limits>
+
+#include <android-base/stringprintf.h>
+#include <utils/String8.h>
+
+using android::base::StringAppendV;
+
+namespace android {
+namespace netdutils {
+
+namespace {
+
+const char kIndentString[] = " ";
+const size_t kIndentStringLen = strlen(kIndentString);
+
+} // namespace
+
+DumpWriter::DumpWriter(int fd) : mIndentLevel(0), mFd(fd) {}
+
+void DumpWriter::incIndent() {
+ if (mIndentLevel < std::numeric_limits<decltype(mIndentLevel)>::max()) {
+ mIndentLevel++;
+ }
+}
+
+void DumpWriter::decIndent() {
+ if (mIndentLevel > std::numeric_limits<decltype(mIndentLevel)>::min()) {
+ mIndentLevel--;
+ }
+}
+
+void DumpWriter::println(const std::string& line) {
+ if (!line.empty()) {
+ for (int i = 0; i < mIndentLevel; i++) {
+ ::write(mFd, kIndentString, kIndentStringLen);
+ }
+ ::write(mFd, line.c_str(), line.size());
+ }
+ ::write(mFd, "\n", 1);
+}
+
+// NOLINTNEXTLINE(cert-dcl50-cpp): Grandfathered C-style variadic function.
+void DumpWriter::println(const char* fmt, ...) {
+ std::string line;
+ va_list ap;
+ va_start(ap, fmt);
+ StringAppendV(&line, fmt, ap);
+ va_end(ap);
+ println(line);
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Fd.cpp b/staticlibs/netd/libnetdutils/Fd.cpp
new file mode 100644
index 0000000..2651f90
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Fd.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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 "netdutils/Fd.h"
+
+namespace android {
+namespace netdutils {
+
+std::ostream& operator<<(std::ostream& os, const Fd& fd) {
+ return os << "Fd[" << fd.get() << "]";
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/FdTest.cpp b/staticlibs/netd/libnetdutils/FdTest.cpp
new file mode 100644
index 0000000..7080f83
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/FdTest.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 <cstdint>
+
+#include <gtest/gtest.h>
+
+#include "netdutils/MockSyscalls.h"
+#include "netdutils/Status.h"
+#include "netdutils/Syscalls.h"
+
+using testing::Mock;
+using testing::Return;
+using testing::StrictMock;
+
+namespace android {
+namespace netdutils {
+namespace {
+
+// Force implicit conversion from UniqueFd -> Fd
+inline Fd toFd(const UniqueFd& fd) {
+ return fd;
+}
+
+} // namespace
+
+TEST(Fd, smoke) {
+ // Expect the following lines to compile
+ Fd fd1(1);
+ Fd fd2(fd1);
+ Fd fd3 = fd2;
+ const Fd fd4(8);
+ const Fd fd5(fd4);
+ const Fd fd6 = fd5;
+ EXPECT_TRUE(isWellFormed(fd3));
+ EXPECT_TRUE(isWellFormed(fd6));
+
+ // Corner case
+ Fd zero(0);
+ EXPECT_TRUE(isWellFormed(zero));
+
+ // Invalid file descriptors
+ Fd bad(-1);
+ Fd weird(-9);
+ EXPECT_FALSE(isWellFormed(bad));
+ EXPECT_FALSE(isWellFormed(weird));
+
+ // Default constructor
+ EXPECT_EQ(Fd(-1), Fd());
+ std::stringstream ss;
+ ss << fd3 << " " << fd6 << " " << bad << " " << weird;
+ EXPECT_EQ("Fd[1] Fd[8] Fd[-1] Fd[-9]", ss.str());
+}
+
+class UniqueFdTest : public testing::Test {
+ protected:
+ StrictMock<ScopedMockSyscalls> mSyscalls;
+};
+
+TEST_F(UniqueFdTest, operatorOstream) {
+ UniqueFd u(97);
+ EXPECT_CALL(mSyscalls, close(toFd(u))).WillOnce(Return(status::ok));
+ std::stringstream ss;
+ ss << u;
+ EXPECT_EQ("UniqueFd[Fd[97]]", ss.str());
+ u.reset();
+}
+
+TEST_F(UniqueFdTest, destructor) {
+ {
+ UniqueFd u(98);
+ EXPECT_CALL(mSyscalls, close(toFd(u))).WillOnce(Return(status::ok));
+ }
+ // Expectation above should be upon leaving nested scope
+ Mock::VerifyAndClearExpectations(&mSyscalls);
+}
+
+TEST_F(UniqueFdTest, reset) {
+ UniqueFd u(99);
+ EXPECT_CALL(mSyscalls, close(toFd(u))).WillOnce(Return(status::ok));
+ u.reset();
+
+ // Expectation above should be upon reset
+ Mock::VerifyAndClearExpectations(&mSyscalls);
+}
+
+TEST_F(UniqueFdTest, moveConstructor) {
+ constexpr Fd kFd(101);
+ UniqueFd u1(kFd);
+ {
+ UniqueFd u2(std::move(u1));
+ // NOLINTNEXTLINE bugprone-use-after-move
+ EXPECT_FALSE(isWellFormed(u1));
+ EXPECT_TRUE(isWellFormed(u2));
+ EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
+ }
+ // Expectation above should be upon leaving nested scope
+ Mock::VerifyAndClearExpectations(&mSyscalls);
+}
+
+TEST_F(UniqueFdTest, moveAssignment) {
+ constexpr Fd kFd(102);
+ UniqueFd u1(kFd);
+ {
+ UniqueFd u2 = std::move(u1);
+ // NOLINTNEXTLINE bugprone-use-after-move
+ EXPECT_FALSE(isWellFormed(u1));
+ EXPECT_TRUE(isWellFormed(u2));
+ UniqueFd u3;
+ u3 = std::move(u2);
+ // NOLINTNEXTLINE bugprone-use-after-move
+ EXPECT_FALSE(isWellFormed(u2));
+ EXPECT_TRUE(isWellFormed(u3));
+ EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
+ }
+ // Expectation above should be upon leaving nested scope
+ Mock::VerifyAndClearExpectations(&mSyscalls);
+}
+
+TEST_F(UniqueFdTest, constConstructor) {
+ constexpr Fd kFd(103);
+ const UniqueFd u(kFd);
+ EXPECT_CALL(mSyscalls, close(toFd(u))).WillOnce(Return(status::ok));
+}
+
+TEST_F(UniqueFdTest, closeFailure) {
+ constexpr Fd kFd(103);
+ UniqueFd u(kFd);
+ EXPECT_CALL(mSyscalls, close(toFd(u))).WillOnce(Return(statusFromErrno(EINTR, "test")));
+ EXPECT_DEBUG_DEATH(u.reset(), "");
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/InternetAddresses.cpp b/staticlibs/netd/libnetdutils/InternetAddresses.cpp
new file mode 100644
index 0000000..322f1b1
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/InternetAddresses.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2018 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 "netdutils/InternetAddresses.h"
+
+#include <string>
+
+#include <android-base/stringprintf.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+namespace android {
+
+using base::StringPrintf;
+
+namespace netdutils {
+
+std::string IPAddress::toString() const noexcept {
+ char repr[INET6_ADDRSTRLEN] = "\0";
+
+ switch (mData.family) {
+ case AF_UNSPEC:
+ return "<unspecified>";
+ case AF_INET: {
+ const in_addr v4 = mData.ip.v4;
+ inet_ntop(AF_INET, &v4, repr, sizeof(repr));
+ break;
+ }
+ case AF_INET6: {
+ const in6_addr v6 = mData.ip.v6;
+ inet_ntop(AF_INET6, &v6, repr, sizeof(repr));
+ break;
+ }
+ default:
+ return "<unknown_family>";
+ }
+
+ if (mData.family == AF_INET6 && mData.scope_id > 0) {
+ return StringPrintf("%s%%%u", repr, mData.scope_id);
+ }
+
+ return repr;
+}
+
+bool IPAddress::forString(const std::string& repr, IPAddress* ip) {
+ const addrinfo hints = {
+ .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV,
+ };
+ addrinfo* res;
+ const int ret = getaddrinfo(repr.c_str(), nullptr, &hints, &res);
+ ScopedAddrinfo res_cleanup(res);
+ if (ret != 0) {
+ return false;
+ }
+
+ bool rval = true;
+ switch (res[0].ai_family) {
+ case AF_INET: {
+ sockaddr_in* sin = (sockaddr_in*) res[0].ai_addr;
+ if (ip) *ip = IPAddress(sin->sin_addr);
+ break;
+ }
+ case AF_INET6: {
+ sockaddr_in6* sin6 = (sockaddr_in6*) res[0].ai_addr;
+ if (ip) *ip = IPAddress(sin6->sin6_addr, sin6->sin6_scope_id);
+ break;
+ }
+ default:
+ rval = false;
+ break;
+ }
+
+ return rval;
+}
+
+IPPrefix::IPPrefix(const IPAddress& ip, int length) : IPPrefix(ip) {
+ // Silently treat CIDR lengths like "-1" as meaning the full bit length
+ // appropriate to the address family.
+ if (length < 0) return;
+ if (length >= mData.cidrlen) return;
+
+ switch (mData.family) {
+ case AF_UNSPEC:
+ break;
+ case AF_INET: {
+ const in_addr_t mask = (length > 0) ? (~0U) << (IPV4_ADDR_BITS - length) : 0U;
+ mData.ip.v4.s_addr &= htonl(mask);
+ mData.cidrlen = static_cast<uint8_t>(length);
+ break;
+ }
+ case AF_INET6: {
+ // The byte in which this CIDR length falls.
+ const int which = length / 8;
+ const int mask = (length % 8 == 0) ? 0 : 0xff << (8 - length % 8);
+ mData.ip.v6.s6_addr[which] &= mask;
+ for (int i = which + 1; i < IPV6_ADDR_LEN; i++) {
+ mData.ip.v6.s6_addr[i] = 0U;
+ }
+ mData.cidrlen = static_cast<uint8_t>(length);
+ break;
+ }
+ default:
+ // TODO: Complain bitterly about possible data corruption?
+ return;
+ }
+}
+
+bool IPPrefix::isUninitialized() const noexcept {
+ static const internal_::compact_ipdata empty{};
+ return mData == empty;
+}
+
+bool IPPrefix::forString(const std::string& repr, IPPrefix* prefix) {
+ size_t index = repr.find('/');
+ if (index == std::string::npos) return false;
+
+ // Parse the IP address.
+ IPAddress ip;
+ if (!IPAddress::forString(repr.substr(0, index), &ip)) return false;
+
+ // Parse the prefix length. Can't use base::ParseUint because it accepts non-base 10 input.
+ const char* prefixString = repr.c_str() + index + 1;
+ if (!isdigit(*prefixString)) return false;
+ char* endptr;
+ unsigned long prefixlen = strtoul(prefixString, &endptr, 10);
+ if (*endptr != '\0') return false;
+
+ uint8_t maxlen = (ip.family() == AF_INET) ? 32 : 128;
+ if (prefixlen > maxlen) return false;
+
+ *prefix = IPPrefix(ip, prefixlen);
+ return true;
+}
+
+std::string IPPrefix::toString() const noexcept {
+ return StringPrintf("%s/%d", ip().toString().c_str(), mData.cidrlen);
+}
+
+std::string IPSockAddr::toString() const noexcept {
+ switch (mData.family) {
+ case AF_INET6:
+ return StringPrintf("[%s]:%u", ip().toString().c_str(), mData.port);
+ default:
+ return StringPrintf("%s:%u", ip().toString().c_str(), mData.port);
+ }
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/InternetAddressesTest.cpp b/staticlibs/netd/libnetdutils/InternetAddressesTest.cpp
new file mode 100644
index 0000000..f75fa76
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/InternetAddressesTest.cpp
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2018 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 <cstdint>
+#include <limits>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <gtest/gtest.h>
+
+#include "netdutils/InternetAddresses.h"
+
+namespace android {
+namespace netdutils {
+namespace {
+
+enum Relation { EQ, LT };
+
+std::ostream& operator<<(std::ostream& os, Relation relation) {
+ switch (relation) {
+ case EQ: os << "eq"; break;
+ case LT: os << "lt"; break;
+ default: os << "?!"; break;
+ }
+ return os;
+}
+
+template <typename T>
+struct OperatorExpectation {
+ const Relation relation;
+ const T obj1;
+ const T obj2;
+
+ std::string toString() const {
+ std::stringstream output;
+ output << obj1 << " " << relation << " " << obj2;
+ return output.str();
+ }
+};
+
+template <typename T>
+void testGamutOfOperators(const OperatorExpectation<T>& expectation) {
+ switch (expectation.relation) {
+ case EQ:
+ EXPECT_TRUE(expectation.obj1 == expectation.obj2);
+ EXPECT_TRUE(expectation.obj1 <= expectation.obj2);
+ EXPECT_TRUE(expectation.obj1 >= expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 != expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 < expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 > expectation.obj2);
+ break;
+
+ case LT:
+ EXPECT_TRUE(expectation.obj1 < expectation.obj2);
+ EXPECT_TRUE(expectation.obj1 <= expectation.obj2);
+ EXPECT_TRUE(expectation.obj1 != expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 > expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 >= expectation.obj2);
+ EXPECT_FALSE(expectation.obj1 == expectation.obj2);
+ break;
+
+ default:
+ FAIL() << "Unknown relation given in test expectation";
+ }
+}
+
+const in_addr IPV4_ANY{htonl(INADDR_ANY)};
+const in_addr IPV4_LOOPBACK{htonl(INADDR_LOOPBACK)};
+const in_addr IPV4_ONES{~0U};
+const in6_addr IPV6_ANY = IN6ADDR_ANY_INIT;
+const in6_addr IPV6_LOOPBACK = IN6ADDR_LOOPBACK_INIT;
+const in6_addr FE80{{{0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}};
+const in6_addr FE80_1{{{0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,1}}};
+const in6_addr FE80_2{{{0xfe,0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,2}}};
+const uint8_t ff = std::numeric_limits<uint8_t>::max();
+const in6_addr IPV6_ONES{{{ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff,ff}}};
+
+TEST(IPAddressTest, GamutOfOperators) {
+ const std::vector<OperatorExpectation<IPAddress>> kExpectations{
+ {EQ, IPAddress(), IPAddress()},
+ {EQ, IPAddress(IPV4_ONES), IPAddress(IPV4_ONES)},
+ {EQ, IPAddress(IPV6_ONES), IPAddress(IPV6_ONES)},
+ {EQ, IPAddress(FE80_1), IPAddress(FE80_1)},
+ {EQ, IPAddress(FE80_2), IPAddress(FE80_2)},
+ {LT, IPAddress(), IPAddress(IPV4_ANY)},
+ {LT, IPAddress(), IPAddress(IPV4_ONES)},
+ {LT, IPAddress(), IPAddress(IPV6_ANY)},
+ {LT, IPAddress(), IPAddress(IPV6_ONES)},
+ {LT, IPAddress(IPV4_ANY), IPAddress(IPV4_ONES)},
+ {LT, IPAddress(IPV4_ANY), IPAddress(IPV6_ANY)},
+ {LT, IPAddress(IPV4_ONES), IPAddress(IPV6_ANY)},
+ {LT, IPAddress(IPV4_ONES), IPAddress(IPV6_ONES)},
+ {LT, IPAddress(IPV6_ANY), IPAddress(IPV6_LOOPBACK)},
+ {LT, IPAddress(IPV6_ANY), IPAddress(IPV6_ONES)},
+ {LT, IPAddress(IPV6_LOOPBACK), IPAddress(IPV6_ONES)},
+ {LT, IPAddress(FE80_1), IPAddress(FE80_2)},
+ {LT, IPAddress(FE80_1), IPAddress(IPV6_ONES)},
+ {LT, IPAddress(FE80_2), IPAddress(IPV6_ONES)},
+ // Sort by scoped_id within the same address.
+ {LT, IPAddress(FE80_1), IPAddress(FE80_1, 1)},
+ {LT, IPAddress(FE80_1, 1), IPAddress(FE80_1, 2)},
+ // Sort by address first, scope_id second.
+ {LT, IPAddress(FE80_1, 2), IPAddress(FE80_2, 1)},
+ };
+
+ size_t tests_run = 0;
+ for (const auto& expectation : kExpectations) {
+ SCOPED_TRACE(expectation.toString());
+ EXPECT_NO_FATAL_FAILURE(testGamutOfOperators(expectation));
+ tests_run++;
+ }
+ EXPECT_EQ(kExpectations.size(), tests_run);
+}
+
+TEST(IPAddressTest, ScopeIds) {
+ // Scope IDs ignored for IPv4 addresses.
+ const IPAddress ones(IPV4_ONES);
+ EXPECT_EQ(0U, ones.scope_id());
+ const IPAddress ones22(ones, 22);
+ EXPECT_EQ(0U, ones22.scope_id());
+ EXPECT_EQ(ones, ones22);
+ const IPAddress ones23(ones, 23);
+ EXPECT_EQ(0U, ones23.scope_id());
+ EXPECT_EQ(ones22, ones23);
+
+ EXPECT_EQ("fe80::1%22", IPAddress(FE80_1, 22).toString());
+ EXPECT_EQ("fe80::2%23", IPAddress(FE80_2, 23).toString());
+
+ // Verify that given an IPAddress with a scope_id an address without a
+ // scope_id can be constructed (just in case it's useful).
+ const IPAddress fe80_intf22(FE80_1, 22);
+ EXPECT_EQ(22U, fe80_intf22.scope_id());
+ EXPECT_EQ(fe80_intf22, IPAddress(fe80_intf22));
+ EXPECT_EQ(IPAddress(FE80_1), IPAddress(fe80_intf22, 0));
+}
+
+TEST(IPAddressTest, forString) {
+ IPAddress ip;
+
+ EXPECT_FALSE(IPAddress::forString("not_an_ip", &ip));
+ EXPECT_FALSE(IPAddress::forString("not_an_ip", nullptr));
+ EXPECT_EQ(IPAddress(), IPAddress::forString("not_an_ip"));
+
+ EXPECT_EQ(IPAddress(IPV4_ANY), IPAddress::forString("0.0.0.0"));
+ EXPECT_EQ(IPAddress(IPV4_ONES), IPAddress::forString("255.255.255.255"));
+ EXPECT_EQ(IPAddress(IPV4_LOOPBACK), IPAddress::forString("127.0.0.1"));
+
+ EXPECT_EQ(IPAddress(IPV6_ANY), IPAddress::forString("::"));
+ EXPECT_EQ(IPAddress(IPV6_ANY), IPAddress::forString("::0"));
+ EXPECT_EQ(IPAddress(IPV6_ANY), IPAddress::forString("0::"));
+ EXPECT_EQ(IPAddress(IPV6_LOOPBACK), IPAddress::forString("::1"));
+ EXPECT_EQ(IPAddress(IPV6_LOOPBACK), IPAddress::forString("0::1"));
+ EXPECT_EQ(IPAddress(FE80_1), IPAddress::forString("fe80::1"));
+ EXPECT_EQ(IPAddress(FE80_1, 22), IPAddress::forString("fe80::1%22"));
+ // This relies upon having a loopback interface named "lo" with ifindex 1.
+ EXPECT_EQ(IPAddress(FE80_1, 1), IPAddress::forString("fe80::1%lo"));
+}
+
+TEST(IPPrefixTest, forString) {
+ IPPrefix prefix;
+
+ EXPECT_FALSE(IPPrefix::forString("", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("invalid", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001::db8::", &prefix));
+
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8:://32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32z", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32/", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/0x20", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8:: /32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/ 32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString(" 2001:db8::/32", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/32 ", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/+32", &prefix));
+
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0/33", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/129", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("192.0.2.0/-1", &prefix));
+ EXPECT_FALSE(IPPrefix::forString("2001:db8::/-1", &prefix));
+
+ EXPECT_TRUE(IPPrefix::forString("2001:db8::/32", &prefix));
+ EXPECT_EQ("2001:db8::/32", prefix.toString());
+ EXPECT_EQ(IPPrefix(IPAddress::forString("2001:db8::"), 32), prefix);
+
+ EXPECT_EQ(IPPrefix(), IPPrefix::forString("invalid"));
+
+ EXPECT_EQ("0.0.0.0/0", IPPrefix::forString("0.0.0.0/0").toString());
+ EXPECT_EQ("::/0", IPPrefix::forString("::/0").toString());
+ EXPECT_EQ("192.0.2.128/25", IPPrefix::forString("192.0.2.131/25").toString());
+ EXPECT_EQ("2001:db8:1:2:3:4:5:4/126",
+ IPPrefix::forString("2001:db8:1:2:3:4:5:6/126").toString());
+}
+
+TEST(IPPrefixTest, IPv4Truncation) {
+ const auto prefixStr = [](int length) -> std::string {
+ return IPPrefix(IPAddress(IPV4_ONES), length).toString();
+ };
+
+ EXPECT_EQ("0.0.0.0/0", prefixStr(0));
+
+ EXPECT_EQ("128.0.0.0/1", prefixStr(1));
+ EXPECT_EQ("192.0.0.0/2", prefixStr(2));
+ EXPECT_EQ("224.0.0.0/3", prefixStr(3));
+ EXPECT_EQ("240.0.0.0/4", prefixStr(4));
+ EXPECT_EQ("248.0.0.0/5", prefixStr(5));
+ EXPECT_EQ("252.0.0.0/6", prefixStr(6));
+ EXPECT_EQ("254.0.0.0/7", prefixStr(7));
+ EXPECT_EQ("255.0.0.0/8", prefixStr(8));
+
+ EXPECT_EQ("255.128.0.0/9", prefixStr(9));
+ EXPECT_EQ("255.192.0.0/10", prefixStr(10));
+ EXPECT_EQ("255.224.0.0/11", prefixStr(11));
+ EXPECT_EQ("255.240.0.0/12", prefixStr(12));
+ EXPECT_EQ("255.248.0.0/13", prefixStr(13));
+ EXPECT_EQ("255.252.0.0/14", prefixStr(14));
+ EXPECT_EQ("255.254.0.0/15", prefixStr(15));
+ EXPECT_EQ("255.255.0.0/16", prefixStr(16));
+
+ EXPECT_EQ("255.255.128.0/17", prefixStr(17));
+ EXPECT_EQ("255.255.192.0/18", prefixStr(18));
+ EXPECT_EQ("255.255.224.0/19", prefixStr(19));
+ EXPECT_EQ("255.255.240.0/20", prefixStr(20));
+ EXPECT_EQ("255.255.248.0/21", prefixStr(21));
+ EXPECT_EQ("255.255.252.0/22", prefixStr(22));
+ EXPECT_EQ("255.255.254.0/23", prefixStr(23));
+ EXPECT_EQ("255.255.255.0/24", prefixStr(24));
+
+ EXPECT_EQ("255.255.255.128/25", prefixStr(25));
+ EXPECT_EQ("255.255.255.192/26", prefixStr(26));
+ EXPECT_EQ("255.255.255.224/27", prefixStr(27));
+ EXPECT_EQ("255.255.255.240/28", prefixStr(28));
+ EXPECT_EQ("255.255.255.248/29", prefixStr(29));
+ EXPECT_EQ("255.255.255.252/30", prefixStr(30));
+ EXPECT_EQ("255.255.255.254/31", prefixStr(31));
+ EXPECT_EQ("255.255.255.255/32", prefixStr(32));
+}
+
+TEST(IPPrefixTest, IPv6Truncation) {
+ const auto prefixStr = [](int length) -> std::string {
+ return IPPrefix(IPAddress(IPV6_ONES), length).toString();
+ };
+
+ EXPECT_EQ("::/0", prefixStr(0));
+
+ EXPECT_EQ("8000::/1", prefixStr(1));
+ EXPECT_EQ("c000::/2", prefixStr(2));
+ EXPECT_EQ("e000::/3", prefixStr(3));
+ EXPECT_EQ("f000::/4", prefixStr(4));
+ EXPECT_EQ("f800::/5", prefixStr(5));
+ EXPECT_EQ("fc00::/6", prefixStr(6));
+ EXPECT_EQ("fe00::/7", prefixStr(7));
+ EXPECT_EQ("ff00::/8", prefixStr(8));
+
+ EXPECT_EQ("ff80::/9", prefixStr(9));
+ EXPECT_EQ("ffc0::/10", prefixStr(10));
+ EXPECT_EQ("ffe0::/11", prefixStr(11));
+ EXPECT_EQ("fff0::/12", prefixStr(12));
+ EXPECT_EQ("fff8::/13", prefixStr(13));
+ EXPECT_EQ("fffc::/14", prefixStr(14));
+ EXPECT_EQ("fffe::/15", prefixStr(15));
+ EXPECT_EQ("ffff::/16", prefixStr(16));
+
+ EXPECT_EQ("ffff:8000::/17", prefixStr(17));
+ EXPECT_EQ("ffff:c000::/18", prefixStr(18));
+ EXPECT_EQ("ffff:e000::/19", prefixStr(19));
+ EXPECT_EQ("ffff:f000::/20", prefixStr(20));
+ EXPECT_EQ("ffff:f800::/21", prefixStr(21));
+ EXPECT_EQ("ffff:fc00::/22", prefixStr(22));
+ EXPECT_EQ("ffff:fe00::/23", prefixStr(23));
+ EXPECT_EQ("ffff:ff00::/24", prefixStr(24));
+
+ EXPECT_EQ("ffff:ff80::/25", prefixStr(25));
+ EXPECT_EQ("ffff:ffc0::/26", prefixStr(26));
+ EXPECT_EQ("ffff:ffe0::/27", prefixStr(27));
+ EXPECT_EQ("ffff:fff0::/28", prefixStr(28));
+ EXPECT_EQ("ffff:fff8::/29", prefixStr(29));
+ EXPECT_EQ("ffff:fffc::/30", prefixStr(30));
+ EXPECT_EQ("ffff:fffe::/31", prefixStr(31));
+ EXPECT_EQ("ffff:ffff::/32", prefixStr(32));
+
+ EXPECT_EQ("ffff:ffff:8000::/33", prefixStr(33));
+ EXPECT_EQ("ffff:ffff:c000::/34", prefixStr(34));
+ EXPECT_EQ("ffff:ffff:e000::/35", prefixStr(35));
+ EXPECT_EQ("ffff:ffff:f000::/36", prefixStr(36));
+ EXPECT_EQ("ffff:ffff:f800::/37", prefixStr(37));
+ EXPECT_EQ("ffff:ffff:fc00::/38", prefixStr(38));
+ EXPECT_EQ("ffff:ffff:fe00::/39", prefixStr(39));
+ EXPECT_EQ("ffff:ffff:ff00::/40", prefixStr(40));
+
+ EXPECT_EQ("ffff:ffff:ff80::/41", prefixStr(41));
+ EXPECT_EQ("ffff:ffff:ffc0::/42", prefixStr(42));
+ EXPECT_EQ("ffff:ffff:ffe0::/43", prefixStr(43));
+ EXPECT_EQ("ffff:ffff:fff0::/44", prefixStr(44));
+ EXPECT_EQ("ffff:ffff:fff8::/45", prefixStr(45));
+ EXPECT_EQ("ffff:ffff:fffc::/46", prefixStr(46));
+ EXPECT_EQ("ffff:ffff:fffe::/47", prefixStr(47));
+ EXPECT_EQ("ffff:ffff:ffff::/48", prefixStr(48));
+
+ EXPECT_EQ("ffff:ffff:ffff:8000::/49", prefixStr(49));
+ EXPECT_EQ("ffff:ffff:ffff:c000::/50", prefixStr(50));
+ EXPECT_EQ("ffff:ffff:ffff:e000::/51", prefixStr(51));
+ EXPECT_EQ("ffff:ffff:ffff:f000::/52", prefixStr(52));
+ EXPECT_EQ("ffff:ffff:ffff:f800::/53", prefixStr(53));
+ EXPECT_EQ("ffff:ffff:ffff:fc00::/54", prefixStr(54));
+ EXPECT_EQ("ffff:ffff:ffff:fe00::/55", prefixStr(55));
+ EXPECT_EQ("ffff:ffff:ffff:ff00::/56", prefixStr(56));
+
+ EXPECT_EQ("ffff:ffff:ffff:ff80::/57", prefixStr(57));
+ EXPECT_EQ("ffff:ffff:ffff:ffc0::/58", prefixStr(58));
+ EXPECT_EQ("ffff:ffff:ffff:ffe0::/59", prefixStr(59));
+ EXPECT_EQ("ffff:ffff:ffff:fff0::/60", prefixStr(60));
+ EXPECT_EQ("ffff:ffff:ffff:fff8::/61", prefixStr(61));
+ EXPECT_EQ("ffff:ffff:ffff:fffc::/62", prefixStr(62));
+ EXPECT_EQ("ffff:ffff:ffff:fffe::/63", prefixStr(63));
+ EXPECT_EQ("ffff:ffff:ffff:ffff::/64", prefixStr(64));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:8000::/65", prefixStr(65));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:c000::/66", prefixStr(66));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:e000::/67", prefixStr(67));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:f000::/68", prefixStr(68));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:f800::/69", prefixStr(69));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fc00::/70", prefixStr(70));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fe00::/71", prefixStr(71));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ff00::/72", prefixStr(72));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ff80::/73", prefixStr(73));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffc0::/74", prefixStr(74));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffe0::/75", prefixStr(75));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fff0::/76", prefixStr(76));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fff8::/77", prefixStr(77));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fffc::/78", prefixStr(78));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:fffe::/79", prefixStr(79));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff::/80", prefixStr(80));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:8000::/81", prefixStr(81));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:c000::/82", prefixStr(82));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:e000::/83", prefixStr(83));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:f000::/84", prefixStr(84));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:f800::/85", prefixStr(85));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fc00::/86", prefixStr(86));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fe00::/87", prefixStr(87));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ff00::/88", prefixStr(88));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ff80::/89", prefixStr(89));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffc0::/90", prefixStr(90));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffe0::/91", prefixStr(91));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fff0::/92", prefixStr(92));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fff8::/93", prefixStr(93));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fffc::/94", prefixStr(94));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:fffe::/95", prefixStr(95));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff::/96", prefixStr(96));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:8000:0/97", prefixStr(97));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:c000:0/98", prefixStr(98));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:e000:0/99", prefixStr(99));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:f000:0/100", prefixStr(100));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:f800:0/101", prefixStr(101));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fc00:0/102", prefixStr(102));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fe00:0/103", prefixStr(103));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ff00:0/104", prefixStr(104));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ff80:0/105", prefixStr(105));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffc0:0/106", prefixStr(106));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffe0:0/107", prefixStr(107));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fff0:0/108", prefixStr(108));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fff8:0/109", prefixStr(109));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fffc:0/110", prefixStr(110));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:fffe:0/111", prefixStr(111));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0/112", prefixStr(112));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:8000/113", prefixStr(113));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:c000/114", prefixStr(114));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:e000/115", prefixStr(115));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:f000/116", prefixStr(116));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:f800/117", prefixStr(117));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fc00/118", prefixStr(118));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fe00/119", prefixStr(119));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00/120", prefixStr(120));
+
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80/121", prefixStr(121));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffc0/122", prefixStr(122));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffe0/123", prefixStr(123));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0/124", prefixStr(124));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff8/125", prefixStr(125));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffc/126", prefixStr(126));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe/127", prefixStr(127));
+ EXPECT_EQ("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128", prefixStr(128));
+}
+
+TEST(IPPrefixTest, TruncationOther) {
+ const struct {
+ const char* ip;
+ const int cidrLen;
+ const char* ipTruncated;
+ } testExpectations[] = {
+ {"192.0.2.0", 24, "192.0.2.0"},
+ {"192.0.2.0", 23, "192.0.2.0"},
+ {"192.0.2.0", 22, "192.0.0.0"},
+ {"192.0.2.0", 1, "128.0.0.0"},
+ {"2001:db8:cafe:d00d::", 56, "2001:db8:cafe:d000::"},
+ {"2001:db8:cafe:d00d::", 48, "2001:db8:cafe::"},
+ {"2001:db8:cafe:d00d::", 47, "2001:db8:cafe::"},
+ {"2001:db8:cafe:d00d::", 46, "2001:db8:cafc::"},
+ };
+
+ for (const auto& expectation : testExpectations) {
+ IPAddress ip;
+ EXPECT_TRUE(IPAddress::forString(expectation.ip, &ip))
+ << "Failed to parse IP address " << expectation.ip;
+
+ IPAddress ipTruncated;
+ EXPECT_TRUE(IPAddress::forString(expectation.ipTruncated, &ipTruncated))
+ << "Failed to parse IP address " << expectation.ipTruncated;
+
+ IPPrefix prefix(ip, expectation.cidrLen);
+
+ EXPECT_EQ(expectation.cidrLen, prefix.length())
+ << "Unexpected cidrLen " << expectation.cidrLen;
+ EXPECT_EQ(ipTruncated, prefix.ip())
+ << "Unexpected IP truncation: " << prefix.ip() << ", expected: " << ipTruncated;
+ }
+}
+
+TEST(IPPrefixTest, GamutOfOperators) {
+ const std::vector<OperatorExpectation<IPPrefix>> kExpectations{
+ {EQ, IPPrefix(), IPPrefix()},
+ {EQ, IPPrefix(IPAddress(IPV4_ANY), 0), IPPrefix(IPAddress(IPV4_ANY), 0)},
+ {EQ, IPPrefix(IPAddress(IPV4_ANY), IPV4_ADDR_BITS), IPPrefix(IPAddress(IPV4_ANY))},
+ {EQ, IPPrefix(IPAddress(IPV6_ANY), 0), IPPrefix(IPAddress(IPV6_ANY), 0)},
+ {EQ, IPPrefix(IPAddress(IPV6_ANY), IPV6_ADDR_BITS), IPPrefix(IPAddress(IPV6_ANY))},
+ // Needlessly fully-specified IPv6 link-local address.
+ {EQ, IPPrefix(IPAddress(FE80_1)), IPPrefix(IPAddress(FE80_1, 0), IPV6_ADDR_BITS)},
+ // Different IPv6 link-local addresses within the same /64, no scoped_id: same /64.
+ {EQ, IPPrefix(IPAddress(FE80_1), 64), IPPrefix(IPAddress(FE80_2), 64)},
+ // Different IPv6 link-local address within the same /64, same scoped_id: same /64.
+ {EQ, IPPrefix(IPAddress(FE80_1, 17), 64), IPPrefix(IPAddress(FE80_2, 17), 64)},
+ // Unspecified < IPv4.
+ {LT, IPPrefix(), IPPrefix(IPAddress(IPV4_ANY), 0)},
+ // Same IPv4 base address sorts by prefix length.
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 0), IPPrefix(IPAddress(IPV4_ANY), 1)},
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 1), IPPrefix(IPAddress(IPV4_ANY), IPV4_ADDR_BITS)},
+ // Truncation means each base IPv4 address is different.
+ {LT, IPPrefix(IPAddress(IPV4_ONES), 0), IPPrefix(IPAddress(IPV4_ONES), 1)},
+ {LT, IPPrefix(IPAddress(IPV4_ONES), 1), IPPrefix(IPAddress(IPV4_ONES), IPV4_ADDR_BITS)},
+ // Sort by base IPv4 addresses first.
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 0), IPPrefix(IPAddress::forString("0.0.0.1"))},
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 1), IPPrefix(IPAddress::forString("0.0.0.1"))},
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 24), IPPrefix(IPAddress::forString("0.0.0.1"))},
+ // IPv4 < IPv6.
+ {LT, IPPrefix(IPAddress(IPV4_ANY), 0), IPPrefix(IPAddress(IPV6_ANY), 0)},
+ {LT, IPPrefix(IPAddress(IPV4_ONES)), IPPrefix(IPAddress(IPV6_ANY))},
+ // Unspecified < IPv6.
+ {LT, IPPrefix(), IPPrefix(IPAddress(IPV6_ANY), 0)},
+ // Same IPv6 base address sorts by prefix length.
+ {LT, IPPrefix(IPAddress(IPV6_ANY), 0), IPPrefix(IPAddress(IPV6_ANY), 1)},
+ {LT, IPPrefix(IPAddress(IPV6_ANY), 1), IPPrefix(IPAddress(IPV6_ANY), IPV6_ADDR_BITS)},
+ // Truncation means each base IPv6 address is different.
+ {LT, IPPrefix(IPAddress(IPV6_ONES), 0), IPPrefix(IPAddress(IPV6_ONES), 1)},
+ {LT, IPPrefix(IPAddress(IPV6_ONES), 1), IPPrefix(IPAddress(IPV6_ONES), IPV6_ADDR_BITS)},
+ // Different IPv6 link-local address in same /64, different scoped_id: different /64.
+ {LT, IPPrefix(IPAddress(FE80_1, 17), 64), IPPrefix(IPAddress(FE80_2, 22), 64)},
+ {LT, IPPrefix(IPAddress(FE80_1, 17), 64), IPPrefix(IPAddress(FE80_1, 18), 64)},
+ {LT, IPPrefix(IPAddress(FE80_1, 18), 64), IPPrefix(IPAddress(FE80_1, 19), 64)},
+ };
+
+ size_t tests_run = 0;
+ for (const auto& expectation : kExpectations) {
+ SCOPED_TRACE(expectation.toString());
+ EXPECT_NO_FATAL_FAILURE(testGamutOfOperators(expectation));
+ tests_run++;
+ }
+ EXPECT_EQ(kExpectations.size(), tests_run);
+}
+
+TEST(IPSockAddrTest, GamutOfOperators) {
+ const std::vector<OperatorExpectation<IPSockAddr>> kExpectations{
+ {EQ, IPSockAddr(), IPSockAddr()},
+ {EQ, IPSockAddr(IPAddress(IPV4_ANY)), IPSockAddr(IPAddress(IPV4_ANY), 0)},
+ {EQ, IPSockAddr(IPAddress(IPV6_ANY)), IPSockAddr(IPAddress(IPV6_ANY), 0)},
+ {EQ, IPSockAddr(IPAddress(FE80_1), 80), IPSockAddr(IPAddress(FE80_1), 80)},
+ {EQ, IPSockAddr(IPAddress(FE80_1, 17)), IPSockAddr(IPAddress(FE80_1, 17), 0)},
+ {LT, IPSockAddr(IPAddress(IPV4_ANY), 0), IPSockAddr(IPAddress(IPV4_ANY), 1)},
+ {LT, IPSockAddr(IPAddress(IPV4_ANY), 53), IPSockAddr(IPAddress(IPV4_ANY), 123)},
+ {LT, IPSockAddr(IPAddress(IPV4_ONES), 123), IPSockAddr(IPAddress(IPV6_ANY), 53)},
+ {LT, IPSockAddr(IPAddress(IPV6_ANY), 0), IPSockAddr(IPAddress(IPV6_ANY), 1)},
+ {LT, IPSockAddr(IPAddress(IPV6_ANY), 53), IPSockAddr(IPAddress(IPV6_ANY), 123)},
+ {LT, IPSockAddr(IPAddress(FE80_1), 80), IPSockAddr(IPAddress(FE80_1, 17), 80)},
+ {LT, IPSockAddr(IPAddress(FE80_1, 17), 80), IPSockAddr(IPAddress(FE80_1, 22), 80)},
+ };
+
+ size_t tests_run = 0;
+ for (const auto& expectation : kExpectations) {
+ SCOPED_TRACE(expectation.toString());
+ EXPECT_NO_FATAL_FAILURE(testGamutOfOperators(expectation));
+ tests_run++;
+ }
+ EXPECT_EQ(kExpectations.size(), tests_run);
+}
+
+TEST(IPSockAddrTest, toString) {
+ EXPECT_EQ("<unspecified>:0", IPSockAddr().toString());
+ EXPECT_EQ("0.0.0.0:0", IPSockAddr(IPAddress(IPV4_ANY)).toString());
+ EXPECT_EQ("255.255.255.255:67", IPSockAddr(IPAddress(IPV4_ONES), 67).toString());
+ EXPECT_EQ("[::]:0", IPSockAddr(IPAddress(IPV6_ANY)).toString());
+ EXPECT_EQ("[::1]:53", IPSockAddr(IPAddress(IPV6_LOOPBACK), 53).toString());
+ EXPECT_EQ("[fe80::1]:0", IPSockAddr(IPAddress(FE80_1)).toString());
+ EXPECT_EQ("[fe80::2%17]:123", IPSockAddr(IPAddress(FE80_2, 17), 123).toString());
+}
+
+TEST(CompatIPDataTest, ConversionsClearUnneededValues) {
+ const uint32_t idx = 17;
+ const IPSockAddr linkLocalNtpSockaddr(IPAddress(FE80_2, idx), 123);
+ EXPECT_EQ(IPAddress(FE80_2, idx), linkLocalNtpSockaddr.ip());
+ // IPSockAddr(IPSockaddr.ip()) see the port cleared.
+ EXPECT_EQ(0, IPSockAddr(linkLocalNtpSockaddr.ip()).port());
+ const IPPrefix linkLocalPrefix(linkLocalNtpSockaddr.ip(), 64);
+ EXPECT_EQ(IPAddress(FE80, idx), linkLocalPrefix.ip());
+ // IPPrefix(IPPrefix.ip()) see the CIDR length cleared.
+ EXPECT_EQ(IPV6_ADDR_BITS, IPPrefix(linkLocalPrefix.ip()).length());
+}
+
+} // namespace
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Log.cpp b/staticlibs/netd/libnetdutils/Log.cpp
new file mode 100644
index 0000000..d2ce98f
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Log.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2018 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 "netdutils/Log.h"
+#include "netdutils/Slice.h"
+
+#include <chrono>
+#include <ctime>
+#include <iomanip>
+#include <mutex>
+#include <sstream>
+
+#include <android-base/strings.h>
+#include <log/log.h>
+
+using ::android::base::Join;
+using ::android::base::StringPrintf;
+
+namespace android {
+namespace netdutils {
+
+namespace {
+
+std::string makeTimestampedEntry(const std::string& entry) {
+ using ::std::chrono::duration_cast;
+ using ::std::chrono::milliseconds;
+ using ::std::chrono::system_clock;
+
+ std::stringstream tsEntry;
+ const auto now = system_clock::now();
+ const auto time_sec = system_clock::to_time_t(now);
+ tsEntry << std::put_time(std::localtime(&time_sec), "%m-%d %H:%M:%S.") << std::setw(3)
+ << std::setfill('0')
+ << duration_cast<milliseconds>(now - system_clock::from_time_t(time_sec)).count() << " "
+ << entry;
+
+ return tsEntry.str();
+}
+
+} // namespace
+
+std::string LogEntry::toString() const {
+ std::vector<std::string> text;
+
+ if (!mMsg.empty()) text.push_back(mMsg);
+ if (!mFunc.empty()) {
+ text.push_back(StringPrintf("%s(%s)", mFunc.c_str(), Join(mArgs, ", ").c_str()));
+ }
+ if (!mReturns.empty()) {
+ text.push_back("->");
+ text.push_back(StringPrintf("(%s)", Join(mReturns, ", ").c_str()));
+ }
+ if (!mUid.empty()) text.push_back(mUid);
+ if (!mDuration.empty()) text.push_back(StringPrintf("(%s)", mDuration.c_str()));
+
+ return Join(text, " ");
+}
+
+LogEntry& LogEntry::message(const std::string& message) {
+ mMsg = message;
+ return *this;
+}
+
+LogEntry& LogEntry::function(const std::string& function_name) {
+ mFunc = function_name;
+ return *this;
+}
+
+LogEntry& LogEntry::prettyFunction(const std::string& pretty_function) {
+ // __PRETTY_FUNCTION__ generally seems to be of the form:
+ //
+ // qualifed::returnType qualified::function(args...)
+ //
+ // where the qualified forms include "(anonymous namespace)" in the
+ // "::"-delimited list and keywords like "virtual" (where applicable).
+ //
+ // Here we try to convert strings like:
+ //
+ // virtual binder::Status android::net::NetdNativeService::isAlive(bool *)
+ // netdutils::LogEntry android::netd::(anonymous namespace)::AAA::BBB::function()
+ //
+ // into just "NetdNativeService::isAlive" or "BBB::function". Note that
+ // without imposing convention, how to easily identify any namespace/class
+ // name boundary is not obvious.
+ const size_t endFuncName = pretty_function.rfind('(');
+ const size_t precedingSpace = pretty_function.rfind(' ', endFuncName);
+ size_t substrStart = (precedingSpace != std::string::npos) ? precedingSpace + 1 : 0;
+
+ const size_t beginFuncName = pretty_function.rfind("::", endFuncName);
+ if (beginFuncName != std::string::npos && substrStart < beginFuncName) {
+ const size_t previousNameBoundary = pretty_function.rfind("::", beginFuncName - 1);
+ if (previousNameBoundary < beginFuncName && substrStart < previousNameBoundary) {
+ substrStart = previousNameBoundary + 2;
+ } else {
+ substrStart = beginFuncName + 2;
+ }
+ }
+
+ mFunc = pretty_function.substr(substrStart, endFuncName - substrStart);
+ return *this;
+}
+
+LogEntry& LogEntry::arg(const std::string& val) {
+ mArgs.push_back(val.empty() ? "\"\"" : val);
+ return *this;
+}
+
+template <>
+LogEntry& LogEntry::arg<>(bool val) {
+ mArgs.push_back(val ? "true" : "false");
+ return *this;
+}
+
+LogEntry& LogEntry::arg(const std::vector<int32_t>& val) {
+ mArgs.push_back(StringPrintf("[%s]", Join(val, ", ").c_str()));
+ return *this;
+}
+
+LogEntry& LogEntry::arg(const std::vector<uint8_t>& val) {
+ mArgs.push_back('{' + toHex(makeSlice(val)) + '}');
+ return *this;
+}
+
+LogEntry& LogEntry::arg(const std::vector<std::string>& val) {
+ mArgs.push_back(StringPrintf("[%s]", Join(val, ", ").c_str()));
+ return *this;
+}
+
+LogEntry& LogEntry::returns(const std::string& rval) {
+ mReturns.push_back(rval);
+ return *this;
+}
+
+LogEntry& LogEntry::returns(bool rval) {
+ mReturns.push_back(rval ? "true" : "false");
+ return *this;
+}
+
+LogEntry& LogEntry::returns(const Status& status) {
+ mReturns.push_back(status.msg());
+ return *this;
+}
+
+LogEntry& LogEntry::withUid(uid_t uid) {
+ mUid = StringPrintf("(uid=%d)", uid);
+ return *this;
+}
+
+LogEntry& LogEntry::withAutomaticDuration() {
+ using ms = std::chrono::duration<float, std::ratio<1, 1000>>;
+
+ const std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
+ std::stringstream duration;
+ duration << std::setprecision(1) << std::chrono::duration_cast<ms>(end - mStart).count()
+ << "ms";
+ mDuration = duration.str();
+ return *this;
+}
+
+LogEntry& LogEntry::withDuration(const std::string& duration) {
+ mDuration = duration;
+ return *this;
+}
+
+Log::~Log() {
+ // TODO: dump the last N entries to the android log for possible posterity.
+ info(LogEntry().function(__FUNCTION__));
+}
+
+void Log::forEachEntry(const std::function<void(const std::string&)>& perEntryFn) const {
+ // We make a (potentially expensive) copy of the log buffer (including
+ // all strings), in case the |perEntryFn| takes its sweet time.
+ std::deque<std::string> entries;
+ {
+ std::shared_lock<std::shared_mutex> guard(mLock);
+ entries.assign(mEntries.cbegin(), mEntries.cend());
+ }
+
+ for (const std::string& entry : entries) perEntryFn(entry);
+}
+
+void Log::record(Log::Level lvl, const std::string& entry) {
+ switch (lvl) {
+ case Level::LOG:
+ break;
+ case Level::INFO:
+ ALOG(LOG_INFO, mTag.c_str(), "%s", entry.c_str());
+ break;
+ case Level::WARN:
+ ALOG(LOG_WARN, mTag.c_str(), "%s", entry.c_str());
+ break;
+ case Level::ERROR:
+ ALOG(LOG_ERROR, mTag.c_str(), "%s", entry.c_str());
+ break;
+ }
+
+ std::lock_guard guard(mLock);
+ mEntries.push_back(makeTimestampedEntry(entry));
+ while (mEntries.size() > mMaxEntries) mEntries.pop_front();
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/LogTest.cpp b/staticlibs/netd/libnetdutils/LogTest.cpp
new file mode 100644
index 0000000..1270560
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/LogTest.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2018 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 <gtest/gtest.h>
+
+#include "netdutils/Log.h"
+
+android::netdutils::LogEntry globalFunctionName() {
+ return android::netdutils::LogEntry().function(__FUNCTION__);
+}
+
+android::netdutils::LogEntry globalPrettyFunctionName() {
+ return android::netdutils::LogEntry().prettyFunction(__PRETTY_FUNCTION__);
+}
+
+namespace android {
+namespace netdutils {
+
+namespace {
+
+LogEntry functionName() {
+ return LogEntry().function(__FUNCTION__);
+}
+
+LogEntry prettyFunctionName() {
+ return LogEntry().prettyFunction(__PRETTY_FUNCTION__);
+}
+
+} // namespace
+
+class AAA {
+ public:
+ AAA() = default;
+
+ LogEntry functionName() {
+ return LogEntry().function(__FUNCTION__);
+ }
+
+ LogEntry prettyFunctionName() {
+ return LogEntry().prettyFunction(__PRETTY_FUNCTION__);
+ }
+
+ class BBB {
+ public:
+ BBB() = default;
+
+ LogEntry functionName() {
+ return LogEntry().function(__FUNCTION__);
+ }
+
+ LogEntry prettyFunctionName() {
+ return LogEntry().prettyFunction(__PRETTY_FUNCTION__);
+ }
+ };
+};
+
+TEST(LogEntryTest, Empty) {
+ LogEntry empty;
+ EXPECT_EQ("", empty.toString());
+}
+
+TEST(LogEntryTest, GlobalFunction) {
+ EXPECT_EQ("globalFunctionName()", ::globalFunctionName().toString());
+}
+
+TEST(LogEntryTest, GlobalPrettyFunction) {
+ EXPECT_EQ("globalPrettyFunctionName()", ::globalPrettyFunctionName().toString());
+}
+
+TEST(LogEntryTest, UnnamedNamespaceFunction) {
+ const LogEntry entry = functionName();
+ EXPECT_EQ("functionName()", entry.toString());
+}
+
+TEST(LogEntryTest, UnnamedNamespacePrettyFunction) {
+ const LogEntry entry = prettyFunctionName();
+ EXPECT_EQ("prettyFunctionName()", entry.toString());
+}
+
+TEST(LogEntryTest, ClassFunction) {
+ const LogEntry entry = AAA().functionName();
+ EXPECT_EQ("functionName()", entry.toString());
+}
+
+TEST(LogEntryTest, ClassPrettyFunction) {
+ const LogEntry entry = AAA().prettyFunctionName();
+ EXPECT_EQ("AAA::prettyFunctionName()", entry.toString());
+}
+
+TEST(LogEntryTest, InnerClassFunction) {
+ const LogEntry entry = AAA::BBB().functionName();
+ EXPECT_EQ("functionName()", entry.toString());
+}
+
+TEST(LogEntryTest, InnerClassPrettyFunction) {
+ const LogEntry entry = AAA::BBB().prettyFunctionName();
+ EXPECT_EQ("BBB::prettyFunctionName()", entry.toString());
+}
+
+TEST(LogEntryTest, PrintChainedArguments) {
+ const LogEntry entry = LogEntry()
+ .function("testFunc")
+ .arg("hello")
+ .arg(42)
+ .arg(true);
+ EXPECT_EQ("testFunc(hello, 42, true)", entry.toString());
+}
+
+TEST(LogEntryTest, PrintIntegralTypes) {
+ const LogEntry entry = LogEntry()
+ .function("testFunc")
+ .arg('A')
+ .arg(100U)
+ .arg(-1000LL);
+ EXPECT_EQ("testFunc(65, 100, -1000)", entry.toString());
+}
+
+TEST(LogEntryTest, PrintHex) {
+ const std::vector<uint8_t> buf{0xDE, 0xAD, 0xBE, 0xEF};
+ const LogEntry entry = LogEntry().function("testFunc").arg(buf);
+ EXPECT_EQ("testFunc({deadbeef})", entry.toString());
+}
+
+TEST(LogEntryTest, PrintArgumentPack) {
+ const LogEntry entry = LogEntry().function("testFunc").args("hello", 42, false);
+ EXPECT_EQ("testFunc(hello, 42, false)", entry.toString());
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/MemBlockTest.cpp b/staticlibs/netd/libnetdutils/MemBlockTest.cpp
new file mode 100644
index 0000000..6455a7e
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/MemBlockTest.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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 <algorithm>
+#include <cstdint>
+#include <utility>
+
+#include <gtest/gtest.h>
+
+#include "netdutils/MemBlock.h"
+#include "netdutils/Slice.h"
+
+namespace android {
+namespace netdutils {
+
+namespace {
+
+constexpr unsigned DNS_PACKET_SIZE = 512;
+constexpr int ARBITRARY_VALUE = 0x55;
+
+MemBlock makeArbitraryMemBlock(size_t len) {
+ MemBlock result(len);
+ // Do some fictional work before returning.
+ for (Slice slice = result.get(); !slice.empty(); slice = drop(slice, 1)) {
+ slice.base()[0] = ARBITRARY_VALUE;
+ }
+ return result;
+}
+
+void checkAllZeros(Slice slice) {
+ for (; !slice.empty(); slice = drop(slice, 1)) {
+ EXPECT_EQ(0U, slice.base()[0]);
+ }
+}
+
+void checkArbitraryMemBlock(const MemBlock& block, size_t expectedSize) {
+ Slice slice = block.get();
+ EXPECT_EQ(expectedSize, slice.size());
+ EXPECT_NE(nullptr, slice.base());
+ for (; !slice.empty(); slice = drop(slice, 1)) {
+ EXPECT_EQ(ARBITRARY_VALUE, slice.base()[0]);
+ }
+}
+
+void checkHelloMello(Slice dest, Slice src) {
+ EXPECT_EQ('h', dest.base()[0]);
+ EXPECT_EQ('e', dest.base()[1]);
+ EXPECT_EQ('l', dest.base()[2]);
+ EXPECT_EQ('l', dest.base()[3]);
+ EXPECT_EQ('o', dest.base()[4]);
+
+ src.base()[0] = 'm';
+ EXPECT_EQ('h', dest.base()[0]);
+}
+
+} // namespace
+
+TEST(MemBlockTest, Empty) {
+ MemBlock empty;
+ EXPECT_TRUE(empty.get().empty());
+ EXPECT_EQ(nullptr, empty.get().base());
+}
+
+TEST(MemBlockTest, ExplicitZero) {
+ MemBlock zero(0);
+ EXPECT_TRUE(zero.get().empty());
+ EXPECT_EQ(nullptr, zero.get().base());
+}
+
+TEST(MemBlockTest, BasicAllocation) {
+ MemBlock dnsPacket(DNS_PACKET_SIZE);
+ Slice slice = dnsPacket.get();
+ EXPECT_EQ(DNS_PACKET_SIZE, slice.size());
+ // Verify the space is '\0'-initialized.
+ ASSERT_NO_FATAL_FAILURE(checkAllZeros(slice));
+ EXPECT_NE(nullptr, slice.base());
+}
+
+TEST(MemBlockTest, MoveConstruction) {
+ MemBlock block(makeArbitraryMemBlock(DNS_PACKET_SIZE));
+ ASSERT_NO_FATAL_FAILURE(checkArbitraryMemBlock(block, DNS_PACKET_SIZE));
+}
+
+TEST(MemBlockTest, MoveAssignmentOrConstruction) {
+ MemBlock block = makeArbitraryMemBlock(DNS_PACKET_SIZE);
+ ASSERT_NO_FATAL_FAILURE(checkArbitraryMemBlock(block, DNS_PACKET_SIZE));
+}
+
+TEST(MemBlockTest, StdMoveAssignment) {
+ constexpr unsigned SIZE = 10;
+
+ MemBlock block;
+ EXPECT_TRUE(block.get().empty());
+ EXPECT_EQ(nullptr, block.get().base());
+
+ {
+ MemBlock block2 = makeArbitraryMemBlock(SIZE);
+ EXPECT_EQ(SIZE, block2.get().size());
+ // More fictional work.
+ for (unsigned i = 0; i < SIZE; i++) {
+ block2.get().base()[i] = i;
+ }
+ block = std::move(block2);
+ }
+
+ EXPECT_EQ(SIZE, block.get().size());
+ for (unsigned i = 0; i < SIZE; i++) {
+ EXPECT_EQ(i, block.get().base()[i]);
+ }
+}
+
+TEST(MemBlockTest, ConstructionFromSlice) {
+ uint8_t data[] = {'h', 'e', 'l', 'l', 'o'};
+ Slice dataSlice(Slice(data, sizeof(data) / sizeof(data[0])));
+
+ MemBlock dataCopy(dataSlice);
+ ASSERT_NO_FATAL_FAILURE(checkHelloMello(dataCopy.get(), dataSlice));
+}
+
+TEST(MemBlockTest, ImplicitCastToSlice) {
+ uint8_t data[] = {'h', 'e', 'l', 'l', 'o'};
+ Slice dataSlice(Slice(data, sizeof(data) / sizeof(data[0])));
+
+ MemBlock dataCopy(dataSlice.size());
+ // NOTE: no explicit MemBlock::get().
+ // Verify the space is '\0'-initialized.
+ ASSERT_NO_FATAL_FAILURE(checkAllZeros(dataCopy));
+ copy(dataCopy, dataSlice);
+ ASSERT_NO_FATAL_FAILURE(checkHelloMello(dataCopy, dataSlice));
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Netfilter.cpp b/staticlibs/netd/libnetdutils/Netfilter.cpp
new file mode 100644
index 0000000..bb43de0
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Netfilter.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+#include <ios>
+
+#include "netdutils/Netfilter.h"
+
+std::ostream& operator<<(std::ostream& os, const nfgenmsg& msg) {
+ return os << std::hex << "nfgenmsg["
+ << "family: 0x" << static_cast<int>(msg.nfgen_family) << ", version: 0x"
+ << static_cast<int>(msg.version) << ", res_id: 0x" << ntohs(msg.res_id) << "]"
+ << std::dec;
+}
diff --git a/staticlibs/netd/libnetdutils/Netlink.cpp b/staticlibs/netd/libnetdutils/Netlink.cpp
new file mode 100644
index 0000000..824c0f2
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Netlink.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <ios>
+#include <linux/netlink.h>
+
+#include "netdutils/Math.h"
+#include "netdutils/Netlink.h"
+
+namespace android {
+namespace netdutils {
+
+void forEachNetlinkMessage(const Slice buf,
+ const std::function<void(const nlmsghdr&, const Slice)>& onMsg) {
+ Slice tail = buf;
+ while (tail.size() >= sizeof(nlmsghdr)) {
+ nlmsghdr hdr = {};
+ extract(tail, hdr);
+ const auto len = std::max<size_t>(hdr.nlmsg_len, sizeof(hdr));
+ onMsg(hdr, drop(take(tail, len), sizeof(hdr)));
+ tail = drop(tail, align(len, 2));
+ }
+}
+
+void forEachNetlinkAttribute(const Slice buf,
+ const std::function<void(const nlattr&, const Slice)>& onAttr) {
+ Slice tail = buf;
+ while (tail.size() >= sizeof(nlattr)) {
+ nlattr hdr = {};
+ extract(tail, hdr);
+ const auto len = std::max<size_t>(hdr.nla_len, sizeof(hdr));
+ onAttr(hdr, drop(take(tail, len), sizeof(hdr)));
+ tail = drop(tail, align(len, 2));
+ }
+}
+
+} // namespace netdutils
+} // namespace android
+
+bool operator==(const sockaddr_nl& lhs, const sockaddr_nl& rhs) {
+ return (lhs.nl_family == rhs.nl_family) && (lhs.nl_pid == rhs.nl_pid) &&
+ (lhs.nl_groups == rhs.nl_groups);
+}
+
+bool operator!=(const sockaddr_nl& lhs, const sockaddr_nl& rhs) {
+ return !(lhs == rhs);
+}
+
+std::ostream& operator<<(std::ostream& os, const nlmsghdr& hdr) {
+ return os << std::hex << "nlmsghdr["
+ << "len: 0x" << hdr.nlmsg_len << ", type: 0x" << hdr.nlmsg_type << ", flags: 0x"
+ << hdr.nlmsg_flags << ", seq: 0x" << hdr.nlmsg_seq << ", pid: 0x" << hdr.nlmsg_pid
+ << "]" << std::dec;
+}
+
+std::ostream& operator<<(std::ostream& os, const nlattr& attr) {
+ return os << std::hex << "nlattr["
+ << "len: 0x" << attr.nla_len << ", type: 0x" << attr.nla_type << "]" << std::dec;
+}
+
+std::ostream& operator<<(std::ostream& os, const sockaddr_nl& addr) {
+ return os << std::hex << "sockaddr_nl["
+ << "family: " << addr.nl_family << ", pid: " << addr.nl_pid
+ << ", groups: " << addr.nl_groups << "]" << std::dec;
+}
diff --git a/staticlibs/netd/libnetdutils/Slice.cpp b/staticlibs/netd/libnetdutils/Slice.cpp
new file mode 100644
index 0000000..7a07d47
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Slice.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 <sstream>
+
+#include "netdutils/Slice.h"
+
+namespace android {
+namespace netdutils {
+namespace {
+
+// Convert one byte to a two character hexadecimal string
+const std::string toHex(uint8_t byte) {
+ const std::array<char, 16> kLookup = {
+ {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}};
+ return {kLookup[byte >> 4], kLookup[byte & 0xf]};
+}
+
+} // namespace
+
+std::string toString(const Slice s) {
+ return std::string(reinterpret_cast<char*>(s.base()), s.size());
+}
+
+std::string toHex(const Slice s, int wrap) {
+ Slice tail = s;
+ int count = 0;
+ std::stringstream ss;
+ while (!tail.empty()) {
+ uint8_t byte = 0;
+ extract(tail, byte);
+ ss << toHex(byte);
+ if ((++count % wrap) == 0) {
+ ss << "\n";
+ }
+ tail = drop(tail, 1);
+ }
+ return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& os, const Slice& slice) {
+ return os << std::hex << "Slice[base: " << reinterpret_cast<void*>(slice.base())
+ << ", limit: " << reinterpret_cast<void*>(slice.limit()) << ", size: 0x"
+ << slice.size() << "]" << std::dec;
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/SliceTest.cpp b/staticlibs/netd/libnetdutils/SliceTest.cpp
new file mode 100644
index 0000000..a496933
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/SliceTest.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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 <array>
+#include <cstdint>
+
+#include <gtest/gtest.h>
+
+#include "netdutils/Slice.h"
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.h"
+
+namespace android {
+namespace netdutils {
+
+class SliceTest : public testing::Test {
+ protected:
+ std::array<char, 256> mRaw = {};
+};
+
+TEST_F(SliceTest, smoke) {
+ Slice s1 = makeSlice(mRaw);
+ Slice s2 = makeSlice(mRaw);
+ auto p = split(s1, 14);
+ s2 = p.first; // avoid warn-unused error
+ std::stringstream ss;
+ ss << Slice();
+ EXPECT_EQ("Slice[base: 0x0, limit: 0x0, size: 0x0]", ss.str());
+ constexpr size_t kBytes = 14;
+ EXPECT_EQ(s1.base(), take(s1, kBytes).base());
+ EXPECT_EQ(kBytes, take(s1, kBytes).size());
+ EXPECT_EQ(s1.base() + kBytes, drop(s1, kBytes).base());
+ EXPECT_EQ(s1.size() - kBytes, drop(s1, kBytes).size());
+ double a = 0;
+ double b = 0;
+ int c = 0;
+ EXPECT_EQ(sizeof(a), extract(s1, a));
+ EXPECT_EQ(sizeof(a) + sizeof(b), extract(s1, a, b));
+ EXPECT_EQ(sizeof(a) + sizeof(b) + sizeof(c), extract(s1, a, b, c));
+}
+
+TEST_F(SliceTest, constructor) {
+ // Expect the following lines to compile
+ Slice s1 = makeSlice(mRaw);
+ Slice s2(s1);
+ Slice s3 = s2;
+ const Slice s4(s3);
+ const Slice s5 = s4;
+ s3 = s5;
+ Slice s6(mRaw.data(), mRaw.size());
+ Slice s7(mRaw.data(), mRaw.data() + mRaw.size());
+ struct {
+ int a;
+ double b;
+ float c;
+ } anon;
+ makeSlice(anon);
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(mRaw.data()), s1.base());
+ EXPECT_EQ(reinterpret_cast<uint8_t*>(mRaw.data()) + mRaw.size(), s1.limit());
+ EXPECT_EQ(mRaw.size(), s1.size());
+ EXPECT_FALSE(mRaw.empty());
+ EXPECT_TRUE(Slice().empty());
+ EXPECT_TRUE(Slice(nullptr, static_cast<size_t>(0)).empty());
+ EXPECT_TRUE(Slice(nullptr, nullptr).empty());
+}
+
+TEST_F(SliceTest, extract) {
+ struct A {
+ int a, b;
+ bool operator==(const A& other) const { return a == other.a && b == other.b; }
+ };
+ struct B {
+ char str[12];
+ bool b;
+ int i;
+ bool operator==(const B& other) const {
+ return b == other.b && i == other.i && 0 == strncmp(str, other.str, 12);
+ }
+ };
+
+ A origA1 = {1, 2};
+ A origA2 = {3, 4};
+ B origB = {"hello world", true, 1234};
+
+ // Populate buffer for extracting.
+ Slice buffer = makeSlice(mRaw);
+ copy(buffer, makeSlice(origA1));
+ copy(drop(buffer, sizeof(origA1)), makeSlice(origB));
+ copy(drop(buffer, sizeof(origA1) + sizeof(origB)), makeSlice(origA2));
+
+ {
+ // Non-variadic extract
+ A a1{};
+ size_t len = extract(buffer, a1);
+ EXPECT_EQ(sizeof(A), len);
+ EXPECT_EQ(origA1, a1);
+ }
+
+ {
+ // Variadic extract, 2 destinations
+ A a1{};
+ B b{};
+ size_t len = extract(buffer, a1, b);
+ EXPECT_EQ(sizeof(A) + sizeof(B), len);
+ EXPECT_EQ(origA1, a1);
+ EXPECT_EQ(origB, b);
+ }
+
+ {
+ // Variadic extract, 3 destinations
+ A a1{}, a2{};
+ B b{};
+ size_t len = extract(buffer, a1, b, a2);
+ EXPECT_EQ(2 * sizeof(A) + sizeof(B), len);
+ EXPECT_EQ(origA1, a1);
+ EXPECT_EQ(origB, b);
+ EXPECT_EQ(origA2, a2);
+ }
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Socket.cpp b/staticlibs/netd/libnetdutils/Socket.cpp
new file mode 100644
index 0000000..e962b6e
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Socket.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 <arpa/inet.h>
+
+#include "netdutils/Slice.h"
+#include "netdutils/Socket.h"
+
+namespace android {
+namespace netdutils {
+
+StatusOr<std::string> toString(const in6_addr& addr) {
+ std::array<char, INET6_ADDRSTRLEN> out = {};
+ auto* rv = inet_ntop(AF_INET6, &addr, out.data(), out.size());
+ if (rv == nullptr) {
+ return statusFromErrno(errno, "inet_ntop() failed");
+ }
+ return std::string(out.data());
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/SocketOption.cpp b/staticlibs/netd/libnetdutils/SocketOption.cpp
new file mode 100644
index 0000000..023df6e
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/SocketOption.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2018 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 "netdutils/SocketOption.h"
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <utility>
+
+#include "netdutils/Syscalls.h"
+
+namespace android {
+namespace netdutils {
+
+Status enableSockopt(Fd sock, int level, int optname) {
+ auto& sys = sSyscalls.get();
+ const int on = 1;
+ return sys.setsockopt(sock, level, optname, &on, sizeof(on));
+}
+
+Status enableTcpKeepAlives(Fd sock, unsigned idleTime, unsigned numProbes, unsigned probeInterval) {
+ RETURN_IF_NOT_OK(enableSockopt(sock, SOL_SOCKET, SO_KEEPALIVE));
+
+ auto& sys = sSyscalls.get();
+ if (idleTime != 0) {
+ RETURN_IF_NOT_OK(sys.setsockopt(sock, SOL_TCP, TCP_KEEPIDLE, &idleTime, sizeof(idleTime)));
+ }
+ if (numProbes != 0) {
+ RETURN_IF_NOT_OK(sys.setsockopt(sock, SOL_TCP, TCP_KEEPCNT, &numProbes, sizeof(numProbes)));
+ }
+ if (probeInterval != 0) {
+ RETURN_IF_NOT_OK(sys.setsockopt(sock, SOL_TCP, TCP_KEEPINTVL, &probeInterval,
+ sizeof(probeInterval)));
+ }
+
+ return status::ok;
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Status.cpp b/staticlibs/netd/libnetdutils/Status.cpp
new file mode 100644
index 0000000..acd8f11
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Status.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "netdutils/Status.h"
+
+#include <sstream>
+
+#include "android-base/stringprintf.h"
+
+namespace android {
+namespace netdutils {
+
+Status statusFromErrno(int err, const std::string& msg) {
+ return Status(err, base::StringPrintf("[%s] : %s", strerror(err), msg.c_str()));
+}
+
+bool equalToErrno(const Status& status, int err) {
+ return status.code() == err;
+}
+
+std::string toString(const Status& status) {
+ std::stringstream ss;
+ ss << status;
+ return ss.str();
+}
+
+std::ostream& operator<<(std::ostream& os, const Status& s) {
+ return os << "Status[code: " << s.code() << ", msg: \"" << s.msg() << "\"]";
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/StatusTest.cpp b/staticlibs/netd/libnetdutils/StatusTest.cpp
new file mode 100644
index 0000000..4cfc3bb
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/StatusTest.cpp
@@ -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.
+ */
+
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.h"
+
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace netdutils {
+namespace {
+
+TEST(StatusTest, valueSemantics) {
+ // Default constructor
+ EXPECT_EQ(status::ok, Status());
+
+ // Copy constructor
+ Status status1(1);
+ Status status2(status1); // NOLINT(performance-unnecessary-copy-initialization)
+ EXPECT_EQ(1, status2.code());
+
+ // Copy assignment
+ Status status3;
+ status3 = status2;
+ EXPECT_EQ(1, status3.code());
+
+ // Same with const objects
+ const Status status4(4);
+ const Status status5(status4); // NOLINT(performance-unnecessary-copy-initialization)
+ Status status6;
+ status6 = status5;
+ EXPECT_EQ(4, status6.code());
+}
+
+TEST(StatusTest, errorMessages) {
+ Status s(42, "for tea too");
+ EXPECT_EQ(42, s.code());
+ EXPECT_FALSE(s.ok());
+ EXPECT_EQ(s.msg(), "for tea too");
+}
+
+TEST(StatusOrTest, moveSemantics) {
+ // Status objects should be cheaply movable.
+ EXPECT_TRUE(std::is_nothrow_move_constructible<Status>::value);
+ EXPECT_TRUE(std::is_nothrow_move_assignable<Status>::value);
+
+ // Should move from a temporary Status (twice)
+ Status s(Status(Status(42, "move me")));
+ EXPECT_EQ(42, s.code());
+ EXPECT_EQ(s.msg(), "move me");
+
+ Status s2(666, "EDAEMON");
+ EXPECT_NE(s, s2);
+ s = s2; // Invokes the move-assignment operator.
+ EXPECT_EQ(666, s.code());
+ EXPECT_EQ(s.msg(), "EDAEMON");
+ EXPECT_EQ(s, s2);
+
+ // A moved-from Status can be re-used.
+ s2 = s;
+
+ // Now both objects are valid.
+ EXPECT_EQ(666, s.code());
+ EXPECT_EQ(s.msg(), "EDAEMON");
+ EXPECT_EQ(s, s2);
+}
+
+TEST(StatusTest, ignoredStatus) {
+ statusFromErrno(ENOTTY, "Not a typewriter, what did you expect?").ignoreError();
+}
+
+TEST(StatusOrTest, ostream) {
+ {
+ StatusOr<int> so(11);
+ std::stringstream ss;
+ ss << so;
+ // TODO: Fix StatusOr to optionally output "value:".
+ EXPECT_EQ("StatusOr[status: Status[code: 0, msg: \"\"]]", ss.str());
+ }
+ {
+ StatusOr<int> err(status::undefined);
+ std::stringstream ss;
+ ss << err;
+ EXPECT_EQ("StatusOr[status: Status[code: 2147483647, msg: \"undefined\"]]", ss.str());
+ }
+}
+
+} // namespace
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Syscalls.cpp b/staticlibs/netd/libnetdutils/Syscalls.cpp
new file mode 100644
index 0000000..9f653f7
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Syscalls.cpp
@@ -0,0 +1,265 @@
+/*
+ * 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 "netdutils/Syscalls.h"
+
+#include <atomic>
+#include <type_traits>
+#include <utility>
+
+namespace android {
+namespace netdutils {
+namespace {
+
+// Retry syscall fn as long as it returns -1 with errno == EINTR
+template <typename FnT, typename... Params>
+typename std::result_of<FnT(Params...)>::type syscallRetry(FnT fn, Params&&... params) {
+ auto rv = fn(std::forward<Params>(params)...);
+ while ((rv == -1) && (errno == EINTR)) {
+ rv = fn(std::forward<Params>(params)...);
+ }
+ return rv;
+}
+
+} // namespace
+
+// Production implementation of Syscalls that forwards to libc syscalls.
+class RealSyscalls final : public Syscalls {
+ public:
+ ~RealSyscalls() override = default;
+
+ StatusOr<UniqueFd> open(const std::string& pathname, int flags, mode_t mode) const override {
+ UniqueFd fd(::open(pathname.c_str(), flags, mode));
+ if (!isWellFormed(fd)) {
+ return statusFromErrno(errno, "open(\"" + pathname + "\"...) failed");
+ }
+ return fd;
+ }
+
+ StatusOr<UniqueFd> socket(int domain, int type, int protocol) const override {
+ UniqueFd sock(::socket(domain, type, protocol));
+ if (!isWellFormed(sock)) {
+ return statusFromErrno(errno, "socket() failed");
+ }
+ return sock;
+ }
+
+ Status getsockname(Fd sock, sockaddr* addr, socklen_t* addrlen) const override {
+ auto rv = ::getsockname(sock.get(), addr, addrlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "getsockname() failed");
+ }
+ return status::ok;
+ }
+
+ Status getsockopt(Fd sock, int level, int optname, void* optval,
+ socklen_t* optlen) const override {
+ auto rv = ::getsockopt(sock.get(), level, optname, optval, optlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "getsockopt() failed");
+ }
+ return status::ok;
+ }
+
+ Status setsockopt(Fd sock, int level, int optname, const void* optval,
+ socklen_t optlen) const override {
+ auto rv = ::setsockopt(sock.get(), level, optname, optval, optlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "setsockopt() failed");
+ }
+ return status::ok;
+ }
+
+ Status bind(Fd sock, const sockaddr* addr, socklen_t addrlen) const override {
+ auto rv = ::bind(sock.get(), addr, addrlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "bind() failed");
+ }
+ return status::ok;
+ }
+
+ Status connect(Fd sock, const sockaddr* addr, socklen_t addrlen) const override {
+ auto rv = syscallRetry(::connect, sock.get(), addr, addrlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "connect() failed");
+ }
+ return status::ok;
+ }
+
+ StatusOr<ifreq> ioctl(Fd sock, unsigned long request, ifreq* ifr) const override {
+ auto rv = ::ioctl(sock.get(), request, ifr);
+ if (rv == -1) {
+ return statusFromErrno(errno, "ioctl() failed");
+ }
+ return *ifr;
+ }
+
+ StatusOr<UniqueFd> eventfd(unsigned int initval, int flags) const override {
+ UniqueFd fd(::eventfd(initval, flags));
+ if (!isWellFormed(fd)) {
+ return statusFromErrno(errno, "eventfd() failed");
+ }
+ return fd;
+ }
+
+ StatusOr<int> ppoll(pollfd* fds, nfds_t nfds, double timeout) const override {
+ timespec ts = {};
+ ts.tv_sec = timeout;
+ ts.tv_nsec = (timeout - ts.tv_sec) * 1e9;
+ auto rv = syscallRetry(::ppoll, fds, nfds, &ts, nullptr);
+ if (rv == -1) {
+ return statusFromErrno(errno, "ppoll() failed");
+ }
+ return rv;
+ }
+
+ StatusOr<size_t> writev(Fd fd, const std::vector<iovec>& iov) const override {
+ auto rv = syscallRetry(::writev, fd.get(), iov.data(), iov.size());
+ if (rv == -1) {
+ return statusFromErrno(errno, "writev() failed");
+ }
+ return rv;
+ }
+
+ StatusOr<size_t> write(Fd fd, const Slice buf) const override {
+ auto rv = syscallRetry(::write, fd.get(), buf.base(), buf.size());
+ if (rv == -1) {
+ return statusFromErrno(errno, "write() failed");
+ }
+ return static_cast<size_t>(rv);
+ }
+
+ StatusOr<Slice> read(Fd fd, const Slice buf) const override {
+ auto rv = syscallRetry(::read, fd.get(), buf.base(), buf.size());
+ if (rv == -1) {
+ return statusFromErrno(errno, "read() failed");
+ }
+ return Slice(buf.base(), rv);
+ }
+
+ StatusOr<size_t> sendto(Fd sock, const Slice buf, int flags, const sockaddr* dst,
+ socklen_t dstlen) const override {
+ auto rv = syscallRetry(::sendto, sock.get(), buf.base(), buf.size(), flags, dst, dstlen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "sendto() failed");
+ }
+ return static_cast<size_t>(rv);
+ }
+
+ StatusOr<Slice> recvfrom(Fd sock, const Slice dst, int flags, sockaddr* src,
+ socklen_t* srclen) const override {
+ auto rv = syscallRetry(::recvfrom, sock.get(), dst.base(), dst.size(), flags, src, srclen);
+ if (rv == -1) {
+ return statusFromErrno(errno, "recvfrom() failed");
+ }
+ if (rv == 0) {
+ return status::eof;
+ }
+ return take(dst, rv);
+ }
+
+ Status shutdown(Fd fd, int how) const override {
+ auto rv = ::shutdown(fd.get(), how);
+ if (rv == -1) {
+ return statusFromErrno(errno, "shutdown() failed");
+ }
+ return status::ok;
+ }
+
+ Status close(Fd fd) const override {
+ auto rv = ::close(fd.get());
+ if (rv == -1) {
+ return statusFromErrno(errno, "close() failed");
+ }
+ return status::ok;
+ }
+
+ StatusOr<UniqueFile> fopen(const std::string& path, const std::string& mode) const override {
+ UniqueFile file(::fopen(path.c_str(), mode.c_str()));
+ if (file == nullptr) {
+ return statusFromErrno(errno, "fopen(\"" + path + "\", \"" + mode + "\") failed");
+ }
+ return file;
+ }
+
+ StatusOr<pid_t> fork() const override {
+ pid_t rv = ::fork();
+ if (rv == -1) {
+ return statusFromErrno(errno, "fork() failed");
+ }
+ return rv;
+ }
+
+ StatusOr<int> vfprintf(FILE* file, const char* format, va_list ap) const override {
+ auto rv = ::vfprintf(file, format, ap);
+ if (rv == -1) {
+ return statusFromErrno(errno, "vfprintf() failed");
+ }
+ return rv;
+ }
+
+ StatusOr<int> vfscanf(FILE* file, const char* format, va_list ap) const override {
+ auto rv = ::vfscanf(file, format, ap);
+ if (rv == -1) {
+ return statusFromErrno(errno, "vfscanf() failed");
+ }
+ return rv;
+ }
+
+ Status fclose(FILE* file) const override {
+ auto rv = ::fclose(file);
+ if (rv == -1) {
+ return statusFromErrno(errno, "fclose() failed");
+ }
+ return status::ok;
+ }
+};
+
+SyscallsHolder::~SyscallsHolder() {
+ delete &get();
+}
+
+Syscalls& SyscallsHolder::get() {
+ while (true) {
+ // memory_order_relaxed gives the compiler and hardware more
+ // freedom. If we get a stale value (this should only happen
+ // early in the execution of a program) the exchange code below
+ // will loop until we get the most current value.
+ auto* syscalls = mSyscalls.load(std::memory_order_relaxed);
+ // Common case returns existing syscalls
+ if (syscalls) {
+ return *syscalls;
+ }
+
+ // This code will execute on first get()
+ std::unique_ptr<Syscalls> tmp(new RealSyscalls());
+ Syscalls* expected = nullptr;
+ bool success = mSyscalls.compare_exchange_strong(expected, tmp.get());
+ if (success) {
+ // Ownership was transferred to mSyscalls already, must release()
+ return *tmp.release();
+ }
+ }
+}
+
+Syscalls& SyscallsHolder::swap(Syscalls& syscalls) {
+ return *mSyscalls.exchange(&syscalls);
+}
+
+SyscallsHolder sSyscalls;
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/SyscallsTest.cpp b/staticlibs/netd/libnetdutils/SyscallsTest.cpp
new file mode 100644
index 0000000..78ffab5
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/SyscallsTest.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 <array>
+#include <cstdint>
+#include <memory>
+
+#include <gtest/gtest.h>
+
+#include "netdutils/Handle.h"
+#include "netdutils/Math.h"
+#include "netdutils/MockSyscalls.h"
+#include "netdutils/Netfilter.h"
+#include "netdutils/Netlink.h"
+#include "netdutils/Slice.h"
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.h"
+#include "netdutils/Syscalls.h"
+
+using testing::_;
+using testing::ByMove;
+using testing::Invoke;
+using testing::Return;
+using testing::StrictMock;
+
+namespace android {
+namespace netdutils {
+
+class SyscallsTest : public testing::Test {
+ protected:
+ StrictMock<ScopedMockSyscalls> mSyscalls;
+};
+
+TEST(syscalls, scopedMock) {
+ auto& old = sSyscalls.get();
+ {
+ StrictMock<ScopedMockSyscalls> s;
+ EXPECT_EQ(&s, &sSyscalls.get());
+ }
+ EXPECT_EQ(&old, &sSyscalls.get());
+}
+
+TEST_F(SyscallsTest, open) {
+ const char kPath[] = "/test/path/please/ignore";
+ constexpr Fd kFd(40);
+ constexpr int kFlags = 883;
+ constexpr mode_t kMode = 37373;
+ const auto& sys = sSyscalls.get();
+ EXPECT_CALL(mSyscalls, open(kPath, kFlags, kMode)).WillOnce(Return(ByMove(UniqueFd(kFd))));
+ EXPECT_CALL(mSyscalls, close(kFd)).WillOnce(Return(status::ok));
+ auto result = sys.open(kPath, kFlags, kMode);
+ EXPECT_EQ(status::ok, result.status());
+ EXPECT_EQ(kFd, result.value());
+}
+
+TEST_F(SyscallsTest, getsockname) {
+ constexpr Fd kFd(40);
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, getsockname(kFd, _, _))
+ .WillOnce(Invoke([expected](Fd, sockaddr* addr, socklen_t* addrlen) {
+ memcpy(addr, &expected, sizeof(expected));
+ EXPECT_EQ(*addrlen, static_cast<socklen_t>(sizeof(expected)));
+ return status::ok;
+ }));
+ const auto result = sys.getsockname<sockaddr_nl>(kFd);
+ EXPECT_TRUE(isOk(result));
+ EXPECT_EQ(expected, result.value());
+
+ // Failure
+ const Status kError = statusFromErrno(EINVAL, "test");
+ EXPECT_CALL(mSyscalls, getsockname(kFd, _, _)).WillOnce(Return(kError));
+ EXPECT_EQ(kError, sys.getsockname<sockaddr_nl>(kFd).status());
+}
+
+TEST_F(SyscallsTest, setsockopt) {
+ constexpr Fd kFd(40);
+ constexpr int kLevel = 50;
+ constexpr int kOptname = 70;
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, setsockopt(kFd, kLevel, kOptname, &expected, sizeof(expected)))
+ .WillOnce(Return(status::ok));
+ EXPECT_EQ(status::ok, sys.setsockopt(kFd, kLevel, kOptname, expected));
+
+ // Failure
+ const Status kError = statusFromErrno(EINVAL, "test");
+ EXPECT_CALL(mSyscalls, setsockopt(kFd, kLevel, kOptname, &expected, sizeof(expected)))
+ .WillOnce(Return(kError));
+ EXPECT_EQ(kError, sys.setsockopt(kFd, kLevel, kOptname, expected));
+}
+
+TEST_F(SyscallsTest, getsockopt) {
+ constexpr Fd kFd(40);
+ constexpr int kLevel = 50;
+ constexpr int kOptname = 70;
+ sockaddr_nl expected = {};
+ socklen_t optLen = 0;
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, getsockopt(kFd, kLevel, kOptname, &expected, &optLen))
+ .WillOnce(Return(status::ok));
+ EXPECT_EQ(status::ok, sys.getsockopt(kFd, kLevel, kOptname, &expected, &optLen));
+
+ // Failure
+ const Status kError = statusFromErrno(EINVAL, "test");
+ EXPECT_CALL(mSyscalls, getsockopt(kFd, kLevel, kOptname, &expected, &optLen))
+ .WillOnce(Return(kError));
+ EXPECT_EQ(kError, sys.getsockopt(kFd, kLevel, kOptname, &expected, &optLen));
+}
+
+TEST_F(SyscallsTest, bind) {
+ constexpr Fd kFd(40);
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, bind(kFd, asSockaddrPtr(&expected), sizeof(expected)))
+ .WillOnce(Return(status::ok));
+ EXPECT_EQ(status::ok, sys.bind(kFd, expected));
+
+ // Failure
+ const Status kError = statusFromErrno(EINVAL, "test");
+ EXPECT_CALL(mSyscalls, bind(kFd, asSockaddrPtr(&expected), sizeof(expected)))
+ .WillOnce(Return(kError));
+ EXPECT_EQ(kError, sys.bind(kFd, expected));
+}
+
+TEST_F(SyscallsTest, connect) {
+ constexpr Fd kFd(40);
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, connect(kFd, asSockaddrPtr(&expected), sizeof(expected)))
+ .WillOnce(Return(status::ok));
+ EXPECT_EQ(status::ok, sys.connect(kFd, expected));
+
+ // Failure
+ const Status kError = statusFromErrno(EINVAL, "test");
+ EXPECT_CALL(mSyscalls, connect(kFd, asSockaddrPtr(&expected), sizeof(expected)))
+ .WillOnce(Return(kError));
+ EXPECT_EQ(kError, sys.connect(kFd, expected));
+}
+
+TEST_F(SyscallsTest, sendto) {
+ constexpr Fd kFd(40);
+ constexpr int kFlags = 0;
+ std::array<char, 10> payload;
+ const auto slice = makeSlice(payload);
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, sendto(kFd, slice, kFlags, asSockaddrPtr(&expected), sizeof(expected)))
+ .WillOnce(Return(slice.size()));
+ EXPECT_EQ(status::ok, sys.sendto(kFd, slice, kFlags, expected));
+}
+
+TEST_F(SyscallsTest, recvfrom) {
+ constexpr Fd kFd(40);
+ constexpr int kFlags = 0;
+ std::array<char, 10> payload;
+ const auto dst = makeSlice(payload);
+ const auto used = take(dst, 8);
+ sockaddr_nl expected = {};
+ auto& sys = sSyscalls.get();
+
+ // Success
+ EXPECT_CALL(mSyscalls, recvfrom(kFd, dst, kFlags, _, _))
+ .WillOnce(Invoke(
+ [expected, used](Fd, const Slice, int, sockaddr* src, socklen_t* srclen) {
+ *srclen = sizeof(expected);
+ memcpy(src, &expected, *srclen);
+ return used;
+ }));
+ auto result = sys.recvfrom<sockaddr_nl>(kFd, dst, kFlags);
+ EXPECT_EQ(status::ok, result.status());
+ EXPECT_EQ(used, result.value().first);
+ EXPECT_EQ(expected, result.value().second);
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/ThreadUtilTest.cpp b/staticlibs/netd/libnetdutils/ThreadUtilTest.cpp
new file mode 100644
index 0000000..8fad8b8
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/ThreadUtilTest.cpp
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2019 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 <string>
+
+#include <android-base/expected.h>
+#include <gtest/gtest.h>
+#include <netdutils/ThreadUtil.h>
+
+namespace android::netdutils {
+
+namespace {
+
+android::base::expected<std::string, int> getThreadName() {
+ char name[16] = {};
+ if (const int ret = pthread_getname_np(pthread_self(), name, sizeof(name)); ret != 0) {
+ return android::base::unexpected(ret);
+ }
+ return std::string(name);
+}
+
+class NoopRun {
+ public:
+ explicit NoopRun(const std::string& name = "") : mName(name) { instanceNum++; }
+
+ // Destructor happens in the thread.
+ ~NoopRun() {
+ if (checkName) {
+ auto expected = getThreadName();
+ EXPECT_TRUE(expected.has_value());
+ EXPECT_EQ(mExpectedName, expected.value());
+ }
+ instanceNum--;
+ }
+
+ void run() {}
+
+ std::string threadName() { return mName; }
+
+ // Set the expected thread name which will be used to check if it matches the actual thread
+ // name which is returned from the system call. The check will happen in the destructor.
+ void setExpectedName(const std::string& expectedName) {
+ checkName = true;
+ mExpectedName = expectedName;
+ }
+
+ static bool waitForAllReleased(int timeoutMs) {
+ constexpr int intervalMs = 20;
+ int limit = timeoutMs / intervalMs;
+ for (int i = 1; i < limit; i++) {
+ if (instanceNum == 0) {
+ return true;
+ }
+ usleep(intervalMs * 1000);
+ }
+ return false;
+ }
+
+ // To track how many instances are alive.
+ static std::atomic<int> instanceNum;
+
+ private:
+ std::string mName;
+ std::string mExpectedName;
+ bool checkName = false;
+};
+
+std::atomic<int> NoopRun::instanceNum;
+
+} // namespace
+
+TEST(ThreadUtilTest, objectReleased) {
+ NoopRun::instanceNum = 0;
+ NoopRun* obj = new NoopRun();
+ EXPECT_EQ(1, NoopRun::instanceNum);
+ threadLaunch(obj);
+
+ // Wait for the object released along with the thread exited.
+ EXPECT_TRUE(NoopRun::waitForAllReleased(1000));
+ EXPECT_EQ(0, NoopRun::instanceNum);
+}
+
+TEST(ThreadUtilTest, SetThreadName) {
+ NoopRun::instanceNum = 0;
+
+ // Test thread name empty.
+ NoopRun* obj1 = new NoopRun();
+ obj1->setExpectedName("");
+
+ // Test normal case.
+ NoopRun* obj2 = new NoopRun("TestName");
+ obj2->setExpectedName("TestName");
+
+ // Test thread name too long.
+ std::string name("TestNameTooooLong");
+ NoopRun* obj3 = new NoopRun(name);
+ obj3->setExpectedName(name.substr(0, 15));
+
+ // Thread names are examined in their destructors.
+ EXPECT_EQ(3, NoopRun::instanceNum);
+ threadLaunch(obj1);
+ threadLaunch(obj2);
+ threadLaunch(obj3);
+
+ EXPECT_TRUE(NoopRun::waitForAllReleased(1000));
+ EXPECT_EQ(0, NoopRun::instanceNum);
+}
+
+} // namespace android::netdutils
diff --git a/staticlibs/netd/libnetdutils/UniqueFd.cpp b/staticlibs/netd/libnetdutils/UniqueFd.cpp
new file mode 100644
index 0000000..1cb30ed
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/UniqueFd.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <algorithm>
+
+#include "netdutils/UniqueFd.h"
+#include "netdutils/Syscalls.h"
+
+namespace android {
+namespace netdutils {
+
+void UniqueFd::reset(Fd fd) {
+ auto& sys = sSyscalls.get();
+ std::swap(fd, mFd);
+ if (isWellFormed(fd)) {
+ expectOk(sys.close(fd));
+ }
+}
+
+std::ostream& operator<<(std::ostream& os, const UniqueFd& fd) {
+ return os << "UniqueFd[" << static_cast<Fd>(fd) << "]";
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/UniqueFile.cpp b/staticlibs/netd/libnetdutils/UniqueFile.cpp
new file mode 100644
index 0000000..21e8779
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/UniqueFile.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 <algorithm>
+
+#include "netdutils/Syscalls.h"
+#include "netdutils/UniqueFile.h"
+
+namespace android {
+namespace netdutils {
+
+void UniqueFileDtor::operator()(FILE* file) const {
+ const auto& sys = sSyscalls.get();
+ sys.fclose(file).ignoreError();
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/Utils.cpp b/staticlibs/netd/libnetdutils/Utils.cpp
new file mode 100644
index 0000000..16ec882
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/Utils.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2021 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 <map>
+
+#include <net/if.h>
+
+#include "dirent.h"
+#include "netdutils/Status.h"
+#include "netdutils/Utils.h"
+
+namespace android {
+namespace netdutils {
+
+StatusOr<std::vector<std::string>> getIfaceNames() {
+ std::vector<std::string> ifaceNames;
+ DIR* d;
+ struct dirent* de;
+
+ if (!(d = opendir("/sys/class/net"))) {
+ return statusFromErrno(errno, "Cannot open iface directory");
+ }
+ while ((de = readdir(d))) {
+ if ((de->d_type != DT_DIR) && (de->d_type != DT_LNK)) continue;
+ if (de->d_name[0] == '.') continue;
+ ifaceNames.push_back(std::string(de->d_name));
+ }
+ closedir(d);
+ return ifaceNames;
+}
+
+StatusOr<std::map<std::string, uint32_t>> getIfaceList() {
+ std::map<std::string, uint32_t> ifacePairs;
+
+ ASSIGN_OR_RETURN(auto ifaceNames, getIfaceNames());
+
+ for (const auto& name : ifaceNames) {
+ uint32_t ifaceIndex = if_nametoindex(name.c_str());
+ if (ifaceIndex) {
+ ifacePairs.insert(std::pair<std::string, uint32_t>(name, ifaceIndex));
+ }
+ }
+ return ifacePairs;
+}
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/BackoffSequence.h b/staticlibs/netd/libnetdutils/include/netdutils/BackoffSequence.h
new file mode 100644
index 0000000..a52e72d
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/BackoffSequence.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2018 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 NETDUTILS_BACKOFFSEQUENCE_H
+#define NETDUTILS_BACKOFFSEQUENCE_H
+
+#include <stdint.h>
+#include <algorithm>
+#include <chrono>
+#include <limits>
+
+namespace android {
+namespace netdutils {
+
+// Encapsulate some RFC 3315 section 14 -style backoff mechanics.
+//
+// https://tools.ietf.org/html/rfc3315#section-14
+template<typename time_type = std::chrono::seconds, typename counter_type = uint32_t>
+class BackoffSequence {
+ public:
+ struct Parameters {
+ time_type initialRetransTime{TIME_UNITY};
+ counter_type maxRetransCount{0U};
+ time_type maxRetransTime{TIME_ZERO};
+ time_type maxRetransDuration{TIME_ZERO};
+ time_type endOfSequenceIndicator{TIME_ZERO};
+ };
+
+ BackoffSequence() : BackoffSequence(Parameters{}) {}
+ BackoffSequence(const BackoffSequence &) = default;
+ BackoffSequence(BackoffSequence &&) = default;
+ BackoffSequence& operator=(const BackoffSequence &) = default;
+ BackoffSequence& operator=(BackoffSequence &&) = default;
+
+ bool hasNextTimeout() const noexcept {
+ return !maxRetransCountExceed() && !maxRetransDurationExceeded();
+ }
+
+ // Returns 0 when the sequence is exhausted.
+ time_type getNextTimeout() {
+ if (!hasNextTimeout()) return getEndOfSequenceIndicator();
+
+ mRetransTime = getNextTimeoutAfter(mRetransTime);
+
+ mRetransCount++;
+ mTotalRetransDuration += mRetransTime;
+ return mRetransTime;
+ }
+
+ time_type getEndOfSequenceIndicator() const noexcept {
+ return mParams.endOfSequenceIndicator;
+ }
+
+ class Builder {
+ public:
+ Builder() {}
+
+ constexpr Builder& withInitialRetransmissionTime(time_type irt) {
+ mParams.initialRetransTime = irt;
+ return *this;
+ }
+ constexpr Builder& withMaximumRetransmissionCount(counter_type mrc) {
+ mParams.maxRetransCount = mrc;
+ return *this;
+ }
+ constexpr Builder& withMaximumRetransmissionTime(time_type mrt) {
+ mParams.maxRetransTime = mrt;
+ return *this;
+ }
+ constexpr Builder& withMaximumRetransmissionDuration(time_type mrd) {
+ mParams.maxRetransDuration = mrd;
+ return *this;
+ }
+ constexpr Builder& withEndOfSequenceIndicator(time_type eos) {
+ mParams.endOfSequenceIndicator = eos;
+ return *this;
+ }
+
+ constexpr BackoffSequence build() const {
+ return BackoffSequence(mParams);
+ }
+
+ private:
+ Parameters mParams;
+ };
+
+ private:
+ static constexpr int PER_ITERATION_SCALING_FACTOR = 2;
+ static constexpr time_type TIME_ZERO = time_type();
+ static constexpr time_type TIME_UNITY = time_type(1);
+
+ constexpr BackoffSequence(const struct Parameters ¶ms)
+ : mParams(params),
+ mRetransCount(0),
+ mRetransTime(TIME_ZERO),
+ mTotalRetransDuration(TIME_ZERO) {}
+
+ constexpr bool maxRetransCountExceed() const {
+ return (mParams.maxRetransCount > 0) && (mRetransCount >= mParams.maxRetransCount);
+ }
+
+ constexpr bool maxRetransDurationExceeded() const {
+ return (mParams.maxRetransDuration > TIME_ZERO) &&
+ (mTotalRetransDuration >= mParams.maxRetransDuration);
+ }
+
+ time_type getNextTimeoutAfter(time_type lastTimeout) const {
+ // TODO: Support proper random jitter. Also, consider supporting some
+ // per-iteration scaling factor other than doubling.
+ time_type nextTimeout = (lastTimeout > TIME_ZERO)
+ ? PER_ITERATION_SCALING_FACTOR * lastTimeout
+ : mParams.initialRetransTime;
+
+ // Check if overflow occurred.
+ if (nextTimeout < lastTimeout) {
+ nextTimeout = std::numeric_limits<time_type>::max();
+ }
+
+ // Cap to maximum allowed, if necessary.
+ if (mParams.maxRetransTime > TIME_ZERO) {
+ nextTimeout = std::min(nextTimeout, mParams.maxRetransTime);
+ }
+
+ // Don't overflow the maximum total duration.
+ if (mParams.maxRetransDuration > TIME_ZERO) {
+ nextTimeout = std::min(nextTimeout, mParams.maxRetransDuration - lastTimeout);
+ }
+ return nextTimeout;
+ }
+
+ const Parameters mParams;
+ counter_type mRetransCount;
+ time_type mRetransTime;
+ time_type mTotalRetransDuration;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETDUTILS_BACKOFFSEQUENCE_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/DumpWriter.h b/staticlibs/netd/libnetdutils/include/netdutils/DumpWriter.h
new file mode 100644
index 0000000..a50b5e6
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/DumpWriter.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 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 NETDUTILS_DUMPWRITER_H_
+#define NETDUTILS_DUMPWRITER_H_
+
+#include <string>
+
+namespace android {
+namespace netdutils {
+
+class DumpWriter {
+ public:
+ DumpWriter(int fd);
+
+ void incIndent();
+ void decIndent();
+
+ void println(const std::string& line);
+ template <size_t n>
+ void println(const char line[n]) {
+ println(std::string(line));
+ }
+ // Hint to the compiler that it should apply printf validation of
+ // arguments (beginning at position 3) of the format (specified in
+ // position 2). Note that position 1 is the implicit "this" argument.
+ void println(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3)));
+ void blankline() { println(""); }
+
+ private:
+ uint8_t mIndentLevel;
+ int mFd;
+};
+
+class ScopedIndent {
+ public:
+ ScopedIndent() = delete;
+ ScopedIndent(const ScopedIndent&) = delete;
+ ScopedIndent(ScopedIndent&&) = delete;
+ explicit ScopedIndent(DumpWriter& dw) : mDw(dw) { mDw.incIndent(); }
+ ~ScopedIndent() { mDw.decIndent(); }
+ ScopedIndent& operator=(const ScopedIndent&) = delete;
+ ScopedIndent& operator=(ScopedIndent&&) = delete;
+
+ // TODO: consider additional {inc,dec}Indent methods and a counter that
+ // can be used to unwind all pending increments on exit.
+
+ private:
+ DumpWriter& mDw;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif // NETDUTILS_DUMPWRITER_H_
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Fd.h b/staticlibs/netd/libnetdutils/include/netdutils/Fd.h
new file mode 100644
index 0000000..7db4087
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Fd.h
@@ -0,0 +1,54 @@
+/*
+ * 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 NETUTILS_FD_H
+#define NETUTILS_FD_H
+
+#include <ostream>
+
+#include "netdutils/Status.h"
+
+namespace android {
+namespace netdutils {
+
+// Strongly typed wrapper for file descriptors with value semantics.
+// This class should typically hold unowned file descriptors.
+class Fd {
+ public:
+ constexpr Fd() = default;
+
+ constexpr Fd(int fd) : mFd(fd) {}
+
+ int get() const { return mFd; }
+
+ bool operator==(const Fd& other) const { return get() == other.get(); }
+ bool operator!=(const Fd& other) const { return get() != other.get(); }
+
+ private:
+ int mFd = -1;
+};
+
+// Return true if fd appears valid (non-negative)
+inline bool isWellFormed(const Fd fd) {
+ return fd.get() >= 0;
+}
+
+std::ostream& operator<<(std::ostream& os, const Fd& fd);
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_FD_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Handle.h b/staticlibs/netd/libnetdutils/include/netdutils/Handle.h
new file mode 100644
index 0000000..82083d4
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Handle.h
@@ -0,0 +1,74 @@
+/*
+ * 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 NETUTILS_HANDLE_H
+#define NETUTILS_HANDLE_H
+
+#include <ostream>
+
+namespace android {
+namespace netdutils {
+
+// Opaque, strongly typed wrapper for integer-like handles.
+// Explicitly avoids implementing arithmetic operations.
+//
+// This class is intended to avoid common errors when reordering
+// arguments to functions, typos and other cases where plain integer
+// types would silently cover up the mistake.
+//
+// usage:
+// DEFINE_HANDLE(ProductId, uint64_t);
+// DEFINE_HANDLE(ThumbnailHash, uint64_t);
+// void foo(ProductId p, ThumbnailHash th) {...}
+//
+// void test() {
+// ProductId p(88);
+// ThumbnailHash th1(100), th2(200);
+//
+// foo(p, th1); <- ok!
+// foo(th1, p); <- disallowed!
+// th1 += 10; <- disallowed!
+// p = th2; <- disallowed!
+// assert(th1 != th2); <- ok!
+// }
+template <typename T, typename TagT>
+class Handle {
+ public:
+ constexpr Handle() = default;
+ constexpr Handle(const T& value) : mValue(value) {}
+
+ const T get() const { return mValue; }
+
+ bool operator==(const Handle& that) const { return get() == that.get(); }
+ bool operator!=(const Handle& that) const { return get() != that.get(); }
+
+ private:
+ T mValue;
+};
+
+#define DEFINE_HANDLE(name, type) \
+ struct _##name##Tag {}; \
+ using name = ::android::netdutils::Handle<type, _##name##Tag>;
+
+template <typename T, typename TagT>
+inline std::ostream& operator<<(std::ostream& os, const Handle<T, TagT>& handle) {
+ return os << handle.get();
+}
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_HANDLE_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/InternetAddresses.h b/staticlibs/netd/libnetdutils/include/netdutils/InternetAddresses.h
new file mode 100644
index 0000000..d5cbe2b
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/InternetAddresses.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdint.h>
+#include <cstring>
+#include <limits>
+#include <string>
+
+#include "netdutils/NetworkConstants.h"
+
+namespace android {
+namespace netdutils {
+
+namespace internal_ {
+
+// A structure to hold data for dealing with Internet addresses (IPAddress) and
+// related types such as IPSockAddr and IPPrefix.
+struct compact_ipdata {
+ uint8_t family{AF_UNSPEC};
+ uint8_t cidrlen{0U}; // written and read in host-byte order
+ in_port_t port{0U}; // written and read in host-byte order
+ uint32_t scope_id{0U};
+ union {
+ in_addr v4;
+ in6_addr v6;
+ } ip{.v6 = IN6ADDR_ANY_INIT}; // written and read in network-byte order
+
+ // Classes that use compact_ipdata and this method should be sure to clear
+ // (i.e. zero or make uniform) any fields not relevant to the class.
+ friend bool operator==(const compact_ipdata& a, const compact_ipdata& b) {
+ if ((a.family != b.family) || (a.cidrlen != b.cidrlen) || (a.port != b.port) ||
+ (a.scope_id != b.scope_id)) {
+ return false;
+ }
+ switch (a.family) {
+ case AF_UNSPEC:
+ // After the above checks, two AF_UNSPEC objects can be
+ // considered equal, for convenience.
+ return true;
+ case AF_INET: {
+ const in_addr v4a = a.ip.v4;
+ const in_addr v4b = b.ip.v4;
+ return (v4a.s_addr == v4b.s_addr);
+ }
+ case AF_INET6: {
+ const in6_addr v6a = a.ip.v6;
+ const in6_addr v6b = b.ip.v6;
+ return IN6_ARE_ADDR_EQUAL(&v6a, &v6b);
+ }
+ }
+ return false;
+ }
+
+ // Classes that use compact_ipdata and this method should be sure to clear
+ // (i.e. zero or make uniform) any fields not relevant to the class.
+ friend bool operator!=(const compact_ipdata& a, const compact_ipdata& b) { return !(a == b); }
+
+ // Classes that use compact_ipdata and this method should be sure to clear
+ // (i.e. zero or make uniform) any fields not relevant to the class.
+ friend bool operator<(const compact_ipdata& a, const compact_ipdata& b) {
+ if (a.family != b.family) return (a.family < b.family);
+ switch (a.family) {
+ case AF_INET: {
+ const in_addr v4a = a.ip.v4;
+ const in_addr v4b = b.ip.v4;
+ if (v4a.s_addr != v4b.s_addr) return (ntohl(v4a.s_addr) < ntohl(v4b.s_addr));
+ break;
+ }
+ case AF_INET6: {
+ const in6_addr v6a = a.ip.v6;
+ const in6_addr v6b = b.ip.v6;
+ const int cmp = std::memcmp(v6a.s6_addr, v6b.s6_addr, IPV6_ADDR_LEN);
+ if (cmp != 0) return cmp < 0;
+ break;
+ }
+ }
+ if (a.cidrlen != b.cidrlen) return (a.cidrlen < b.cidrlen);
+ if (a.port != b.port) return (a.port < b.port);
+ return (a.scope_id < b.scope_id);
+ }
+};
+
+static_assert(AF_UNSPEC <= std::numeric_limits<uint8_t>::max(), "AF_UNSPEC value too large");
+static_assert(AF_INET <= std::numeric_limits<uint8_t>::max(), "AF_INET value too large");
+static_assert(AF_INET6 <= std::numeric_limits<uint8_t>::max(), "AF_INET6 value too large");
+static_assert(sizeof(compact_ipdata) == 24U, "compact_ipdata unexpectedly large");
+
+} // namespace internal_
+
+struct AddrinfoDeleter {
+ void operator()(struct addrinfo* p) const {
+ if (p != nullptr) {
+ freeaddrinfo(p);
+ }
+ }
+};
+
+typedef std::unique_ptr<struct addrinfo, struct AddrinfoDeleter> ScopedAddrinfo;
+
+inline bool usesScopedIds(const in6_addr& ipv6) {
+ return (IN6_IS_ADDR_LINKLOCAL(&ipv6) || IN6_IS_ADDR_MC_LINKLOCAL(&ipv6));
+}
+
+class IPPrefix;
+class IPSockAddr;
+
+class IPAddress {
+ public:
+ static bool forString(const std::string& repr, IPAddress* ip);
+ static IPAddress forString(const std::string& repr) {
+ IPAddress ip;
+ if (!forString(repr, &ip)) return IPAddress();
+ return ip;
+ }
+
+ IPAddress() = default;
+ IPAddress(const IPAddress&) = default;
+ IPAddress(IPAddress&&) = default;
+
+ explicit IPAddress(const in_addr& ipv4)
+ : mData({AF_INET, IPV4_ADDR_BITS, 0U, 0U, {.v4 = ipv4}}) {}
+ explicit IPAddress(const in6_addr& ipv6)
+ : mData({AF_INET6, IPV6_ADDR_BITS, 0U, 0U, {.v6 = ipv6}}) {}
+ IPAddress(const in6_addr& ipv6, uint32_t scope_id)
+ : mData({AF_INET6,
+ IPV6_ADDR_BITS,
+ 0U,
+ // Sanity check: scoped_ids only for link-local addresses.
+ usesScopedIds(ipv6) ? scope_id : 0U,
+ {.v6 = ipv6}}) {}
+ IPAddress(const IPAddress& ip, uint32_t scope_id) : IPAddress(ip) {
+ mData.scope_id = (family() == AF_INET6 && usesScopedIds(mData.ip.v6)) ? scope_id : 0U;
+ }
+
+ IPAddress& operator=(const IPAddress&) = default;
+ IPAddress& operator=(IPAddress&&) = default;
+
+ constexpr sa_family_t family() const noexcept { return mData.family; }
+ constexpr uint32_t scope_id() const noexcept { return mData.scope_id; }
+
+ std::string toString() const noexcept;
+
+ friend std::ostream& operator<<(std::ostream& os, const IPAddress& ip) {
+ os << ip.toString();
+ return os;
+ }
+ friend bool operator==(const IPAddress& a, const IPAddress& b) { return (a.mData == b.mData); }
+ friend bool operator!=(const IPAddress& a, const IPAddress& b) { return (a.mData != b.mData); }
+ friend bool operator<(const IPAddress& a, const IPAddress& b) { return (a.mData < b.mData); }
+ friend bool operator>(const IPAddress& a, const IPAddress& b) { return (b.mData < a.mData); }
+ friend bool operator<=(const IPAddress& a, const IPAddress& b) { return (a < b) || (a == b); }
+ friend bool operator>=(const IPAddress& a, const IPAddress& b) { return (b < a) || (a == b); }
+
+ private:
+ friend class IPPrefix;
+ friend class IPSockAddr;
+
+ explicit IPAddress(const internal_::compact_ipdata& ipdata) : mData(ipdata) {
+ mData.port = 0U;
+ switch (mData.family) {
+ case AF_INET:
+ mData.cidrlen = IPV4_ADDR_BITS;
+ mData.scope_id = 0U;
+ break;
+ case AF_INET6:
+ mData.cidrlen = IPV6_ADDR_BITS;
+ if (usesScopedIds(ipdata.ip.v6)) mData.scope_id = ipdata.scope_id;
+ break;
+ default:
+ mData.cidrlen = 0U;
+ mData.scope_id = 0U;
+ break;
+ }
+ }
+
+ internal_::compact_ipdata mData{};
+};
+
+class IPPrefix {
+ public:
+ static bool forString(const std::string& repr, IPPrefix* prefix);
+ static IPPrefix forString(const std::string& repr) {
+ IPPrefix prefix;
+ if (!forString(repr, &prefix)) return IPPrefix();
+ return prefix;
+ }
+
+ IPPrefix() = default;
+ IPPrefix(const IPPrefix&) = default;
+ IPPrefix(IPPrefix&&) = default;
+
+ explicit IPPrefix(const IPAddress& ip) : mData(ip.mData) {}
+
+ // Truncate the IP address |ip| at length |length|. Lengths greater than
+ // the address-family-relevant maximum, along with negative values, are
+ // interpreted as if the address-family-relevant maximum had been given.
+ IPPrefix(const IPAddress& ip, int length);
+
+ IPPrefix& operator=(const IPPrefix&) = default;
+ IPPrefix& operator=(IPPrefix&&) = default;
+
+ constexpr sa_family_t family() const noexcept { return mData.family; }
+ IPAddress ip() const noexcept { return IPAddress(mData); }
+ in_addr addr4() const noexcept { return mData.ip.v4; }
+ in6_addr addr6() const noexcept { return mData.ip.v6; }
+ constexpr int length() const noexcept { return mData.cidrlen; }
+
+ bool isUninitialized() const noexcept;
+ std::string toString() const noexcept;
+
+ friend std::ostream& operator<<(std::ostream& os, const IPPrefix& prefix) {
+ os << prefix.toString();
+ return os;
+ }
+ friend bool operator==(const IPPrefix& a, const IPPrefix& b) { return (a.mData == b.mData); }
+ friend bool operator!=(const IPPrefix& a, const IPPrefix& b) { return (a.mData != b.mData); }
+ friend bool operator<(const IPPrefix& a, const IPPrefix& b) { return (a.mData < b.mData); }
+ friend bool operator>(const IPPrefix& a, const IPPrefix& b) { return (b.mData < a.mData); }
+ friend bool operator<=(const IPPrefix& a, const IPPrefix& b) { return (a < b) || (a == b); }
+ friend bool operator>=(const IPPrefix& a, const IPPrefix& b) { return (b < a) || (a == b); }
+
+ private:
+ internal_::compact_ipdata mData{};
+};
+
+// An Internet socket address.
+//
+// Cannot represent other types of socket addresses (e.g. UNIX socket address, et cetera).
+class IPSockAddr {
+ public:
+ // TODO: static forString
+
+ static IPSockAddr toIPSockAddr(const std::string& repr, in_port_t port) {
+ return IPSockAddr(IPAddress::forString(repr), port);
+ }
+ static IPSockAddr toIPSockAddr(const sockaddr& sa) {
+ switch (sa.sa_family) {
+ case AF_INET:
+ return IPSockAddr(*reinterpret_cast<const sockaddr_in*>(&sa));
+ case AF_INET6:
+ return IPSockAddr(*reinterpret_cast<const sockaddr_in6*>(&sa));
+ default:
+ return IPSockAddr();
+ }
+ }
+ static IPSockAddr toIPSockAddr(const sockaddr_storage& ss) {
+ return toIPSockAddr(*reinterpret_cast<const sockaddr*>(&ss));
+ }
+
+ IPSockAddr() = default;
+ IPSockAddr(const IPSockAddr&) = default;
+ IPSockAddr(IPSockAddr&&) = default;
+
+ explicit IPSockAddr(const IPAddress& ip) : mData(ip.mData) {}
+ IPSockAddr(const IPAddress& ip, in_port_t port) : mData(ip.mData) { mData.port = port; }
+ explicit IPSockAddr(const sockaddr_in& ipv4sa)
+ : IPSockAddr(IPAddress(ipv4sa.sin_addr), ntohs(ipv4sa.sin_port)) {}
+ explicit IPSockAddr(const sockaddr_in6& ipv6sa)
+ : IPSockAddr(IPAddress(ipv6sa.sin6_addr, ipv6sa.sin6_scope_id), ntohs(ipv6sa.sin6_port)) {}
+
+ IPSockAddr& operator=(const IPSockAddr&) = default;
+ IPSockAddr& operator=(IPSockAddr&&) = default;
+
+ constexpr sa_family_t family() const noexcept { return mData.family; }
+ IPAddress ip() const noexcept { return IPAddress(mData); }
+ constexpr in_port_t port() const noexcept { return mData.port; }
+
+ // Implicit conversion to sockaddr_storage.
+ operator sockaddr_storage() const noexcept {
+ sockaddr_storage ss;
+ ss.ss_family = mData.family;
+ switch (mData.family) {
+ case AF_INET:
+ reinterpret_cast<sockaddr_in*>(&ss)->sin_addr = mData.ip.v4;
+ reinterpret_cast<sockaddr_in*>(&ss)->sin_port = htons(mData.port);
+ break;
+ case AF_INET6:
+ reinterpret_cast<sockaddr_in6*>(&ss)->sin6_addr = mData.ip.v6;
+ reinterpret_cast<sockaddr_in6*>(&ss)->sin6_port = htons(mData.port);
+ reinterpret_cast<sockaddr_in6*>(&ss)->sin6_scope_id = mData.scope_id;
+ break;
+ }
+ return ss;
+ }
+
+ std::string toString() const noexcept;
+
+ friend std::ostream& operator<<(std::ostream& os, const IPSockAddr& prefix) {
+ os << prefix.toString();
+ return os;
+ }
+ friend bool operator==(const IPSockAddr& a, const IPSockAddr& b) {
+ return (a.mData == b.mData);
+ }
+ friend bool operator!=(const IPSockAddr& a, const IPSockAddr& b) {
+ return (a.mData != b.mData);
+ }
+ friend bool operator<(const IPSockAddr& a, const IPSockAddr& b) { return (a.mData < b.mData); }
+ friend bool operator>(const IPSockAddr& a, const IPSockAddr& b) { return (b.mData < a.mData); }
+ friend bool operator<=(const IPSockAddr& a, const IPSockAddr& b) { return (a < b) || (a == b); }
+ friend bool operator>=(const IPSockAddr& a, const IPSockAddr& b) { return (b < a) || (a == b); }
+
+ private:
+ internal_::compact_ipdata mData{};
+};
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Log.h b/staticlibs/netd/libnetdutils/include/netdutils/Log.h
new file mode 100644
index 0000000..77ae649
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Log.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2018 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 NETUTILS_LOG_H
+#define NETUTILS_LOG_H
+
+#include <chrono>
+#include <deque>
+#include <shared_mutex>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+#include <android-base/thread_annotations.h>
+
+#include <netdutils/Status.h>
+
+namespace android {
+namespace netdutils {
+
+class LogEntry {
+ public:
+ LogEntry() = default;
+ LogEntry(const LogEntry&) = default;
+ LogEntry(LogEntry&&) = default;
+ ~LogEntry() = default;
+ LogEntry& operator=(const LogEntry&) = default;
+ LogEntry& operator=(LogEntry&&) = default;
+
+ std::string toString() const;
+
+ ///
+ // Helper methods that make it easy to build up a LogEntry message.
+ // If performance becomes a factor the implementations could be inlined.
+ ///
+ LogEntry& message(const std::string& message);
+
+ // For calling with __FUNCTION__.
+ LogEntry& function(const std::string& function_name);
+ // For calling with __PRETTY_FUNCTION__.
+ LogEntry& prettyFunction(const std::string& pretty_function);
+
+ // Convenience methods for each of the common types of function arguments.
+ LogEntry& arg(const std::string& val);
+ // Intended for binary buffers, formats as hex
+ LogEntry& arg(const std::vector<uint8_t>& val);
+ LogEntry& arg(const std::vector<int32_t>& val);
+ LogEntry& arg(const std::vector<std::string>& val);
+ template <typename IntT, typename = std::enable_if_t<std::is_arithmetic_v<IntT>>>
+ LogEntry& arg(IntT val) {
+ mArgs.push_back(std::to_string(val));
+ return *this;
+ }
+ // Not using a plain overload here to avoid the implicit conversion from
+ // any pointer to bool, which causes string literals to print as 'true'.
+ template <>
+ LogEntry& arg<>(bool val);
+
+ template <typename... Args>
+ LogEntry& args(const Args&... a) {
+ // Cleverness ahead: we throw away the initializer_list filled with
+ // zeroes, all we care about is calling arg() for each argument.
+ (void) std::initializer_list<int>{(arg(a), 0)...};
+ return *this;
+ }
+
+ // Some things can return more than one value, or have multiple output
+ // parameters, so each of these adds to the mReturns vector.
+ LogEntry& returns(const std::string& rval);
+ LogEntry& returns(const Status& status);
+ LogEntry& returns(bool rval);
+ template <class T>
+ LogEntry& returns(T val) {
+ mReturns.push_back(std::to_string(val));
+ return *this;
+ }
+
+ LogEntry& withUid(uid_t uid);
+
+ // Append the duration computed since the creation of this instance.
+ LogEntry& withAutomaticDuration();
+ // Append the string-ified duration computed by some other means.
+ LogEntry& withDuration(const std::string& duration);
+
+ private:
+ std::chrono::steady_clock::time_point mStart = std::chrono::steady_clock::now();
+ std::string mMsg{};
+ std::string mFunc{};
+ std::vector<std::string> mArgs{};
+ std::vector<std::string> mReturns{};
+ std::string mUid{};
+ std::string mDuration{};
+};
+
+class Log {
+ public:
+ Log() = delete;
+ Log(const std::string& tag) : Log(tag, MAX_ENTRIES) {}
+ Log(const std::string& tag, size_t maxEntries) : mTag(tag), mMaxEntries(maxEntries) {}
+ Log(const Log&) = delete;
+ Log(Log&&) = delete;
+ ~Log();
+ Log& operator=(const Log&) = delete;
+ Log& operator=(Log&&) = delete;
+
+ LogEntry newEntry() const { return LogEntry(); }
+
+ // Record a log entry in internal storage only.
+ void log(const std::string& entry) { record(Level::LOG, entry); }
+ template <size_t n>
+ void log(const char entry[n]) { log(std::string(entry)); }
+ void log(const LogEntry& entry) { log(entry.toString()); }
+ void log(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ using ::android::base::StringAppendV;
+ std::string result;
+ va_list ap;
+ va_start(ap, fmt);
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ log(result);
+ }
+
+ // Record a log entry in internal storage and to ALOGI as well.
+ void info(const std::string& entry) { record(Level::INFO, entry); }
+ template <size_t n>
+ void info(const char entry[n]) { info(std::string(entry)); }
+ void info(const LogEntry& entry) { info(entry.toString()); }
+ void info(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ using ::android::base::StringAppendV;
+ std::string result;
+ va_list ap;
+ va_start(ap, fmt);
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ info(result);
+ }
+
+ // Record a log entry in internal storage and to ALOGW as well.
+ void warn(const std::string& entry) { record(Level::WARN, entry); }
+ template <size_t n>
+ void warn(const char entry[n]) { warn(std::string(entry)); }
+ void warn(const LogEntry& entry) { warn(entry.toString()); }
+ void warn(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ using ::android::base::StringAppendV;
+ std::string result;
+ va_list ap;
+ va_start(ap, fmt);
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ warn(result);
+ }
+
+ // Record a log entry in internal storage and to ALOGE as well.
+ void error(const std::string& entry) { record(Level::ERROR, entry); }
+ template <size_t n>
+ void error(const char entry[n]) { error(std::string(entry)); }
+ void error(const LogEntry& entry) { error(entry.toString()); }
+ void error(const char* fmt, ...) __attribute__((__format__(__printf__, 2, 3))) {
+ using ::android::base::StringAppendV;
+ std::string result;
+ va_list ap;
+ va_start(ap, fmt);
+ StringAppendV(&result, fmt, ap);
+ va_end(ap);
+ error(result);
+ }
+
+ // Iterates over every entry in the log in chronological order. Operates
+ // on a copy of the log entries, and so perEntryFn may itself call one of
+ // the logging functions if needed.
+ void forEachEntry(const std::function<void(const std::string&)>& perEntryFn) const;
+
+ private:
+ static constexpr const size_t MAX_ENTRIES = 750U;
+ const std::string mTag;
+ const size_t mMaxEntries;
+
+ // The LOG level adds an entry to mEntries but does not output the message
+ // to the system log. All other levels append to mEntries and output to the
+ // the system log.
+ enum class Level {
+ LOG,
+ INFO,
+ WARN,
+ ERROR,
+ };
+
+ void record(Level lvl, const std::string& entry);
+
+ mutable std::shared_mutex mLock;
+ std::deque<const std::string> mEntries; // GUARDED_BY(mLock), when supported
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_LOG_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Math.h b/staticlibs/netd/libnetdutils/include/netdutils/Math.h
new file mode 100644
index 0000000..c41fbf5
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Math.h
@@ -0,0 +1,40 @@
+/*
+ * 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 NETUTILS_MATH_H
+#define NETUTILS_MATH_H
+
+#include <algorithm>
+#include <cstdint>
+
+namespace android {
+namespace netdutils {
+
+template <class T>
+inline constexpr const T mask(const int shift) {
+ return (1 << shift) - 1;
+}
+
+// Align x up to the nearest integer multiple of 2^shift
+template <class T>
+inline constexpr const T align(const T& x, const int shift) {
+ return (x + mask<T>(shift)) & ~mask<T>(shift);
+}
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_MATH_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/MemBlock.h b/staticlibs/netd/libnetdutils/include/netdutils/MemBlock.h
new file mode 100644
index 0000000..fd4d612
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/MemBlock.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2018 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 NETUTILS_MEMBLOCK_H
+#define NETUTILS_MEMBLOCK_H
+
+#include <memory>
+#include "netdutils/Slice.h"
+
+namespace android {
+namespace netdutils {
+
+// A class to encapsulate self-deleting byte arrays while preserving access
+// to the underlying length (without the length being part of the type, e.g.
+// std::array<>). By design, the only interface to the underlying bytes is
+// via Slice, to encourage safer memory access usage.
+//
+// No thread-safety guarantees whatsoever.
+class MemBlock {
+ public:
+ MemBlock() : MemBlock(0U) {}
+ explicit MemBlock(size_t len)
+ : mData((len > 0U) ? new uint8_t[len]{} : nullptr),
+ mLen(len) {}
+ // Allocate memory of size src.size() and copy src into this MemBlock.
+ explicit MemBlock(Slice src) : MemBlock(src.size()) {
+ copy(get(), src);
+ }
+
+ // No copy construction or assignment.
+ MemBlock(const MemBlock&) = delete;
+ MemBlock& operator=(const MemBlock&) = delete;
+
+ // Move construction and assignment are okay.
+ MemBlock(MemBlock&&) = default;
+ MemBlock& operator=(MemBlock&&) = default;
+
+ // Even though this method is const, the memory wrapped by the
+ // returned Slice is mutable.
+ Slice get() const noexcept { return Slice(mData.get(), mLen); }
+
+ // Implicit cast to Slice.
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator const Slice() const noexcept { return get(); }
+
+ private:
+ std::unique_ptr<uint8_t[]> mData;
+ size_t mLen;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_MEMBLOCK_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Misc.h b/staticlibs/netd/libnetdutils/include/netdutils/Misc.h
new file mode 100644
index 0000000..d344f81
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Misc.h
@@ -0,0 +1,63 @@
+/*
+ * 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 NETUTILS_MISC_H
+#define NETUTILS_MISC_H
+
+#include <map>
+
+namespace android {
+namespace netdutils {
+
+// Lookup key in map, returing a default value if key is not found
+template <typename U, typename V>
+inline const V& findWithDefault(const std::map<U, V>& map, const U& key, const V& dflt) {
+ auto it = map.find(key);
+ return (it == map.end()) ? dflt : it->second;
+}
+
+// Movable, copiable, scoped lambda (or std::function) runner. Useful
+// for running arbitrary cleanup or logging code when exiting a scope.
+//
+// Compare to defer in golang.
+template <typename FnT>
+class Cleanup {
+ public:
+ Cleanup() = delete;
+ explicit Cleanup(FnT fn) : mFn(fn) {}
+ ~Cleanup() { if (!mReleased) mFn(); }
+
+ void release() { mReleased = true; }
+
+ private:
+ bool mReleased{false};
+ FnT mFn;
+};
+
+// Helper to make a new Cleanup. Avoids complex or impossible syntax
+// when wrapping lambdas.
+//
+// Usage:
+// auto cleanup = makeCleanup([](){ your_code_here; });
+template <typename FnT>
+Cleanup<FnT> makeCleanup(FnT fn) {
+ return Cleanup<FnT>(fn);
+}
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_MISC_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/MockSyscalls.h b/staticlibs/netd/libnetdutils/include/netdutils/MockSyscalls.h
new file mode 100644
index 0000000..f57b55c
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/MockSyscalls.h
@@ -0,0 +1,90 @@
+/*
+ * 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 NETUTILS_MOCK_SYSCALLS_H
+#define NETUTILS_MOCK_SYSCALLS_H
+
+#include <atomic>
+#include <cassert>
+#include <memory>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "netdutils/Syscalls.h"
+
+namespace android {
+namespace netdutils {
+
+class MockSyscalls : public Syscalls {
+ public:
+ virtual ~MockSyscalls() = default;
+ // Use Return(ByMove(...)) to deal with movable return types.
+ MOCK_CONST_METHOD3(open,
+ StatusOr<UniqueFd>(const std::string& pathname, int flags, mode_t mode));
+ MOCK_CONST_METHOD3(socket, StatusOr<UniqueFd>(int domain, int type, int protocol));
+ MOCK_CONST_METHOD3(getsockname, Status(Fd sock, sockaddr* addr, socklen_t* addrlen));
+ MOCK_CONST_METHOD5(getsockopt, Status(Fd sock, int level, int optname, void* optval,
+ socklen_t *optlen));
+ MOCK_CONST_METHOD5(setsockopt, Status(Fd sock, int level, int optname, const void* optval,
+ socklen_t optlen));
+
+ MOCK_CONST_METHOD3(bind, Status(Fd sock, const sockaddr* addr, socklen_t addrlen));
+ MOCK_CONST_METHOD3(connect, Status(Fd sock, const sockaddr* addr, socklen_t addrlen));
+ MOCK_CONST_METHOD3(ioctl, StatusOr<ifreq>(Fd sock, unsigned long request, ifreq* ifr));
+
+ // Use Return(ByMove(...)) to deal with movable return types.
+ MOCK_CONST_METHOD2(eventfd, StatusOr<UniqueFd>(unsigned int initval, int flags));
+ MOCK_CONST_METHOD3(ppoll, StatusOr<int>(pollfd* fds, nfds_t nfds, double timeout));
+
+ MOCK_CONST_METHOD2(writev, StatusOr<size_t>(Fd fd, const std::vector<iovec>& iov));
+ MOCK_CONST_METHOD2(write, StatusOr<size_t>(Fd fd, const Slice buf));
+ MOCK_CONST_METHOD2(read, StatusOr<Slice>(Fd fd, const Slice buf));
+ MOCK_CONST_METHOD5(sendto, StatusOr<size_t>(Fd sock, const Slice buf, int flags,
+ const sockaddr* dst, socklen_t dstlen));
+ MOCK_CONST_METHOD5(recvfrom, StatusOr<Slice>(Fd sock, const Slice dst, int flags, sockaddr* src,
+ socklen_t* srclen));
+ MOCK_CONST_METHOD2(shutdown, Status(Fd fd, int how));
+ MOCK_CONST_METHOD1(close, Status(Fd fd));
+
+ MOCK_CONST_METHOD2(fopen,
+ StatusOr<UniqueFile>(const std::string& path, const std::string& mode));
+ MOCK_CONST_METHOD3(vfprintf, StatusOr<int>(FILE* file, const char* format, va_list ap));
+ MOCK_CONST_METHOD3(vfscanf, StatusOr<int>(FILE* file, const char* format, va_list ap));
+ MOCK_CONST_METHOD1(fclose, Status(FILE* file));
+ MOCK_CONST_METHOD0(fork, StatusOr<pid_t>());
+};
+
+// For the lifetime of this mock, replace the contents of sSyscalls
+// with a pointer to this mock. Behavior is undefined if multiple
+// ScopedMockSyscalls instances exist concurrently.
+class ScopedMockSyscalls : public MockSyscalls {
+ public:
+ ScopedMockSyscalls() : mOld(sSyscalls.swap(*this)) { assert((mRefcount++) == 1); }
+ virtual ~ScopedMockSyscalls() {
+ sSyscalls.swap(mOld);
+ assert((mRefcount--) == 0);
+ }
+
+ private:
+ std::atomic<int> mRefcount{0};
+ Syscalls& mOld;
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_MOCK_SYSCALLS_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Netfilter.h b/staticlibs/netd/libnetdutils/include/netdutils/Netfilter.h
new file mode 100644
index 0000000..22736f1
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Netfilter.h
@@ -0,0 +1,28 @@
+/*
+ * 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 NETUTILS_NETFILTER_H
+#define NETUTILS_NETFILTER_H
+
+#include <ostream>
+
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netlink.h>
+
+std::ostream& operator<<(std::ostream& os, const nfgenmsg& msg);
+
+#endif /* NETUTILS_NETFILTER_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Netlink.h b/staticlibs/netd/libnetdutils/include/netdutils/Netlink.h
new file mode 100644
index 0000000..ee5183a
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Netlink.h
@@ -0,0 +1,55 @@
+/*
+ * 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 NETUTILS_NETLINK_H
+#define NETUTILS_NETLINK_H
+
+#include <functional>
+#include <ostream>
+#include <linux/netlink.h>
+
+#include "netdutils/Slice.h"
+
+namespace android {
+namespace netdutils {
+
+// Invoke onMsg once for each netlink message in buf. onMsg will be
+// invoked with an aligned and deserialized header along with a Slice
+// containing the message payload.
+//
+// Assume that the first message begins at offset zero within buf.
+void forEachNetlinkMessage(const Slice buf,
+ const std::function<void(const nlmsghdr&, const Slice)>& onMsg);
+
+// Invoke onAttr once for each netlink attribute in buf. onAttr will be
+// invoked with an aligned and deserialized header along with a Slice
+// containing the attribute payload.
+//
+// Assume that the first attribute begins at offset zero within buf.
+void forEachNetlinkAttribute(const Slice buf,
+ const std::function<void(const nlattr&, const Slice)>& onAttr);
+
+} // namespace netdutils
+} // namespace android
+
+bool operator==(const sockaddr_nl& lhs, const sockaddr_nl& rhs);
+bool operator!=(const sockaddr_nl& lhs, const sockaddr_nl& rhs);
+
+std::ostream& operator<<(std::ostream& os, const nlmsghdr& hdr);
+std::ostream& operator<<(std::ostream& os, const nlattr& attr);
+std::ostream& operator<<(std::ostream& os, const sockaddr_nl& addr);
+
+#endif /* NETUTILS_NETLINK_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/NetworkConstants.h b/staticlibs/netd/libnetdutils/include/netdutils/NetworkConstants.h
new file mode 100644
index 0000000..dead9a1
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/NetworkConstants.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#pragma once
+
+namespace android {
+namespace netdutils {
+
+// See also NetworkConstants.java in frameworks/base.
+constexpr int IPV4_ADDR_LEN = 4;
+constexpr int IPV4_ADDR_BITS = 32;
+constexpr int IPV6_ADDR_LEN = 16;
+constexpr int IPV6_ADDR_BITS = 128;
+
+// Referred from SHA256_DIGEST_LENGTH in boringssl
+constexpr size_t SHA256_SIZE = 32;
+
+} // namespace netdutils
+} // namespace android
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/ResponseCode.h b/staticlibs/netd/libnetdutils/include/netdutils/ResponseCode.h
new file mode 100644
index 0000000..c170684
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/ResponseCode.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 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 NETDUTILS_RESPONSECODE_H
+#define NETDUTILS_RESPONSECODE_H
+
+namespace android {
+namespace netdutils {
+
+class ResponseCode {
+ // Keep in sync with
+ // frameworks/base/services/java/com/android/server/NetworkManagementService.java
+ public:
+ // 100 series - Requestion action was initiated; expect another reply
+ // before proceeding with a new command.
+ // clang-format off
+ static constexpr int ActionInitiated = 100;
+ static constexpr int InterfaceListResult = 110;
+ static constexpr int TetherInterfaceListResult = 111;
+ static constexpr int TetherDnsFwdTgtListResult = 112;
+ static constexpr int TtyListResult = 113;
+ static constexpr int TetheringStatsListResult = 114;
+ static constexpr int TetherDnsFwdNetIdResult = 115;
+
+ // 200 series - Requested action has been successfully completed
+ static constexpr int CommandOkay = 200;
+ static constexpr int TetherStatusResult = 210;
+ static constexpr int IpFwdStatusResult = 211;
+ static constexpr int InterfaceGetCfgResult = 213;
+ // Formerly: int SoftapStatusResult = 214;
+ static constexpr int UsbRNDISStatusResult = 215;
+ static constexpr int InterfaceRxCounterResult = 216;
+ static constexpr int InterfaceTxCounterResult = 217;
+ static constexpr int InterfaceRxThrottleResult = 218;
+ static constexpr int InterfaceTxThrottleResult = 219;
+ static constexpr int QuotaCounterResult = 220;
+ static constexpr int TetheringStatsResult = 221;
+ // NOTE: keep synced with bionic/libc/dns/net/gethnamaddr.c
+ static constexpr int DnsProxyQueryResult = 222;
+ static constexpr int ClatdStatusResult = 223;
+
+ // 400 series - The command was accepted but the requested action
+ // did not take place.
+ static constexpr int OperationFailed = 400;
+ static constexpr int DnsProxyOperationFailed = 401;
+ static constexpr int ServiceStartFailed = 402;
+ static constexpr int ServiceStopFailed = 403;
+
+ // 500 series - The command was not accepted and the requested
+ // action did not take place.
+ static constexpr int CommandSyntaxError = 500;
+ static constexpr int CommandParameterError = 501;
+
+ // 600 series - Unsolicited broadcasts
+ static constexpr int InterfaceChange = 600;
+ static constexpr int BandwidthControl = 601;
+ static constexpr int ServiceDiscoveryFailed = 602;
+ static constexpr int ServiceDiscoveryServiceAdded = 603;
+ static constexpr int ServiceDiscoveryServiceRemoved = 604;
+ static constexpr int ServiceRegistrationFailed = 605;
+ static constexpr int ServiceRegistrationSucceeded = 606;
+ static constexpr int ServiceResolveFailed = 607;
+ static constexpr int ServiceResolveSuccess = 608;
+ static constexpr int ServiceSetHostnameFailed = 609;
+ static constexpr int ServiceSetHostnameSuccess = 610;
+ static constexpr int ServiceGetAddrInfoFailed = 611;
+ static constexpr int ServiceGetAddrInfoSuccess = 612;
+ static constexpr int InterfaceClassActivity = 613;
+ static constexpr int InterfaceAddressChange = 614;
+ static constexpr int InterfaceDnsInfo = 615;
+ static constexpr int RouteChange = 616;
+ static constexpr int StrictCleartext = 617;
+ // clang-format on
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif // NETDUTILS_RESPONSECODE_H
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Slice.h b/staticlibs/netd/libnetdutils/include/netdutils/Slice.h
new file mode 100644
index 0000000..717fbd1
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Slice.h
@@ -0,0 +1,161 @@
+/*
+ * 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 NETUTILS_SLICE_H
+#define NETUTILS_SLICE_H
+
+#include <algorithm>
+#include <array>
+#include <cstring>
+#include <ostream>
+#include <tuple>
+#include <vector>
+
+namespace android {
+namespace netdutils {
+
+// Immutable wrapper for a linear region of unowned bytes.
+// Slice represents memory as a half-closed interval [base, limit).
+//
+// Note that without manually invoking the Slice() constructor, it is
+// impossible to increase the size of a slice. This guarantees that
+// applications that properly use the slice API will never access
+// memory outside of a slice.
+//
+// Note that const Slice still wraps mutable memory, however copy
+// assignment and move assignment to slice are disabled.
+class Slice {
+ public:
+ Slice() = default;
+
+ // Create a slice beginning at base and continuing to but not including limit
+ Slice(void* base, void* limit) : mBase(toUint8(base)), mLimit(toUint8(limit)) {}
+
+ // Create a slice beginning at base and continuing for size bytes
+ Slice(void* base, size_t size) : Slice(base, toUint8(base) + size) {}
+
+ // Return the address of the first byte in this slice
+ uint8_t* base() const { return mBase; }
+
+ // Return the address of the first byte following this slice
+ uint8_t* limit() const { return mLimit; }
+
+ // Return the size of this slice in bytes
+ size_t size() const { return limit() - base(); }
+
+ // Return true if size() == 0
+ bool empty() const { return base() == limit(); }
+
+ private:
+ static uint8_t* toUint8(void* ptr) { return reinterpret_cast<uint8_t*>(ptr); }
+
+ uint8_t* mBase = nullptr;
+ uint8_t* mLimit = nullptr;
+};
+
+// Return slice representation of ref which must be a POD type
+template <typename T>
+inline const Slice makeSlice(const T& ref) {
+ static_assert(std::is_pod<T>::value, "value must be a POD type");
+ static_assert(!std::is_pointer<T>::value, "value must not be a pointer type");
+ return {const_cast<T*>(&ref), sizeof(ref)};
+}
+
+// Return slice representation of string data()
+inline const Slice makeSlice(const std::string& s) {
+ using ValueT = std::string::value_type;
+ return {const_cast<ValueT*>(s.data()), s.size() * sizeof(ValueT)};
+}
+
+// Return slice representation of vector data()
+template <typename T>
+inline const Slice makeSlice(const std::vector<T>& v) {
+ return {const_cast<T*>(v.data()), v.size() * sizeof(T)};
+}
+
+// Return slice representation of array data()
+template <typename U, size_t V>
+inline const Slice makeSlice(const std::array<U, V>& a) {
+ return {const_cast<U*>(a.data()), a.size() * sizeof(U)};
+}
+
+// Return prefix and suffix of Slice s ending and starting at position cut
+inline std::pair<const Slice, const Slice> split(const Slice s, size_t cut) {
+ const size_t tmp = std::min(cut, s.size());
+ return {{s.base(), s.base() + tmp}, {s.base() + tmp, s.limit()}};
+}
+
+// Return prefix of Slice s ending at position cut
+inline const Slice take(const Slice s, size_t cut) {
+ return std::get<0>(split(s, cut));
+}
+
+// Return suffix of Slice s starting at position cut
+inline const Slice drop(const Slice s, size_t cut) {
+ return std::get<1>(split(s, cut));
+}
+
+// Copy from src into dst. Bytes copied is the lesser of dst.size() and src.size()
+inline size_t copy(const Slice dst, const Slice src) {
+ const auto min = std::min(dst.size(), src.size());
+ memcpy(dst.base(), src.base(), min);
+ return min;
+}
+
+// Base case for variadic extract below
+template <typename Head>
+inline size_t extract(const Slice src, Head& head) {
+ return copy(makeSlice(head), src);
+}
+
+// Copy from src into one or more pointers to POD data. If src.size()
+// is less than the sum of all data pointers a suffix of data will be
+// left unmodified. Return the number of bytes copied.
+template <typename Head, typename... Tail>
+inline size_t extract(const Slice src, Head& head, Tail&... tail) {
+ const auto extracted = extract(src, head);
+ return extracted + extract(drop(src, extracted), tail...);
+}
+
+// Return a string containing a copy of the contents of s
+std::string toString(const Slice s);
+
+// Return a string containing a hexadecimal representation of the contents of s.
+// This function inserts a newline into its output every wrap bytes.
+std::string toHex(const Slice s, int wrap = INT_MAX);
+
+inline bool operator==(const Slice& lhs, const Slice& rhs) {
+ return (lhs.base() == rhs.base()) && (lhs.limit() == rhs.limit());
+}
+
+inline bool operator!=(const Slice& lhs, const Slice& rhs) {
+ return !(lhs == rhs);
+}
+
+std::ostream& operator<<(std::ostream& os, const Slice& slice);
+
+// Return suffix of Slice s starting at the first match of byte c. If no matched
+// byte, return an empty Slice.
+inline const Slice findFirstMatching(const Slice s, uint8_t c) {
+ uint8_t* match = (uint8_t*)memchr(s.base(), c, s.size());
+ if (!match) return Slice();
+ return drop(s, match - s.base());
+}
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_SLICE_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Socket.h b/staticlibs/netd/libnetdutils/include/netdutils/Socket.h
new file mode 100644
index 0000000..e5aaab9
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Socket.h
@@ -0,0 +1,44 @@
+/*
+ * 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 NETDUTILS_SOCKET_H
+#define NETDUTILS_SOCKET_H
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string>
+
+#include "netdutils/StatusOr.h"
+
+namespace android {
+namespace netdutils {
+
+inline sockaddr* asSockaddrPtr(void* addr) {
+ return reinterpret_cast<sockaddr*>(addr);
+}
+
+inline const sockaddr* asSockaddrPtr(const void* addr) {
+ return reinterpret_cast<const sockaddr*>(addr);
+}
+
+// Return a string representation of addr or Status if there was a
+// failure during conversion.
+StatusOr<std::string> toString(const in6_addr& addr);
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETDUTILS_SOCKET_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/SocketOption.h b/staticlibs/netd/libnetdutils/include/netdutils/SocketOption.h
new file mode 100644
index 0000000..3b0aab7
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/SocketOption.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2018 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 NETDUTILS_SOCKETOPTION_H
+#define NETDUTILS_SOCKETOPTION_H
+
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string>
+
+#include "netdutils/Fd.h"
+#include "netdutils/Status.h"
+
+namespace android {
+namespace netdutils {
+
+// Turn on simple "boolean" socket options.
+//
+// This is simple wrapper for options that are enabled via code of the form:
+//
+// int on = 1;
+// setsockopt(..., &on, sizeof(on));
+Status enableSockopt(Fd sock, int level, int optname);
+
+// Turn on TCP keepalives, and set keepalive parameters for this socket.
+//
+// A parameter value of zero does not set that parameter.
+//
+// Typical system defaults are:
+//
+// idleTime (in seconds)
+// $ cat /proc/sys/net/ipv4/tcp_keepalive_time
+// 7200
+//
+// numProbes
+// $ cat /proc/sys/net/ipv4/tcp_keepalive_probes
+// 9
+//
+// probeInterval (in seconds)
+// $ cat /proc/sys/net/ipv4/tcp_keepalive_intvl
+// 75
+Status enableTcpKeepAlives(Fd sock, unsigned idleTime, unsigned numProbes, unsigned probeInterval);
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETDUTILS_SOCKETOPTION_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Status.h b/staticlibs/netd/libnetdutils/include/netdutils/Status.h
new file mode 100644
index 0000000..bc347d5
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Status.h
@@ -0,0 +1,136 @@
+/*
+ * 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 NETUTILS_STATUS_H
+#define NETUTILS_STATUS_H
+
+#include <cassert>
+#include <limits>
+#include <ostream>
+
+#include <android-base/result.h>
+
+namespace android {
+namespace netdutils {
+
+// Simple status implementation suitable for use on the stack in low
+// or moderate performance code. This can definitely be improved but
+// for now short string optimization is expected to keep the common
+// success case fast.
+//
+// Status is implicitly movable via the default noexcept move constructor
+// and noexcept move-assignment operator.
+class [[nodiscard]] Status {
+ public:
+ Status() = default;
+ explicit Status(int code) : mCode(code) {}
+
+ // Constructs an error Status, |code| must be non-zero.
+ Status(int code, std::string msg) : mCode(code), mMsg(std::move(msg)) { assert(!ok()); }
+
+ Status(android::base::Result<void> result)
+ : mCode(result.ok() ? 0 : result.error().code()),
+ mMsg(result.ok() ? "" : result.error().message()) {}
+
+ int code() const { return mCode; }
+
+ bool ok() const { return code() == 0; }
+
+ const std::string& msg() const { return mMsg; }
+
+ // Explicitly ignores the Status without triggering [[nodiscard]] errors.
+ void ignoreError() const {}
+
+ bool operator==(const Status& other) const { return code() == other.code(); }
+ bool operator!=(const Status& other) const { return !(*this == other); }
+
+ private:
+ int mCode = 0;
+ std::string mMsg;
+};
+
+namespace status {
+
+const Status ok{0};
+// EOF is not part of errno space, we'll place it far above the
+// highest existing value.
+const Status eof{0x10001, "end of file"};
+const Status undefined{std::numeric_limits<int>::max(), "undefined"};
+
+} // namespace status
+
+// Return true if status is "OK". This is sometimes preferable to
+// status.ok() when we want to check the state of Status-like objects
+// that implicitly cast to Status.
+inline bool isOk(const Status& status) {
+ return status.ok();
+}
+
+// For use only in tests. Used for both Status and Status-like objects. See also isOk().
+#define EXPECT_OK(status) EXPECT_TRUE(isOk(status))
+#define ASSERT_OK(status) ASSERT_TRUE(isOk(status))
+
+// Documents that status is expected to be ok. This function may log
+// (or assert when running in debug mode) if status has an unexpected value.
+inline void expectOk(const Status& /*status*/) {
+ // TODO: put something here, for now this function serves solely as documentation.
+}
+
+// Convert POSIX errno to a Status object.
+// If Status is extended to have more features, this mapping may
+// become more complex.
+Status statusFromErrno(int err, const std::string& msg);
+
+// Helper that checks Status-like object (notably StatusOr) against a
+// value in the errno space.
+bool equalToErrno(const Status& status, int err);
+
+// Helper that converts Status-like object (notably StatusOr) to a
+// message.
+std::string toString(const Status& status);
+
+std::ostream& operator<<(std::ostream& os, const Status& s);
+
+// Evaluate 'stmt' to a Status object and if it results in an error, return that
+// error. Use 'tmp' as a variable name to avoid shadowing any variables named
+// tmp.
+#define RETURN_IF_NOT_OK_IMPL(tmp, stmt) \
+ do { \
+ ::android::netdutils::Status tmp = (stmt); \
+ if (!isOk(tmp)) { \
+ return tmp; \
+ } \
+ } while (false)
+
+// Create a unique variable name to avoid shadowing local variables.
+#define RETURN_IF_NOT_OK_CONCAT(line, stmt) RETURN_IF_NOT_OK_IMPL(__CONCAT(_status_, line), stmt)
+
+// Macro to allow exception-like handling of error return values.
+//
+// If the evaluation of stmt results in an error, return that error
+// from current function.
+//
+// Example usage:
+// Status bar() { ... }
+//
+// RETURN_IF_NOT_OK(status);
+// RETURN_IF_NOT_OK(bar());
+#define RETURN_IF_NOT_OK(stmt) RETURN_IF_NOT_OK_CONCAT(__LINE__, stmt)
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_STATUS_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/StatusOr.h b/staticlibs/netd/libnetdutils/include/netdutils/StatusOr.h
new file mode 100644
index 0000000..c7aa4e4
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/StatusOr.h
@@ -0,0 +1,119 @@
+/*
+ * 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 NETUTILS_STATUSOR_H
+#define NETUTILS_STATUSOR_H
+
+#include <cassert>
+#include "netdutils/Status.h"
+
+namespace android {
+namespace netdutils {
+
+// Wrapper around a combination of Status and application value type.
+// T may be any copyable or movable type.
+template <typename T>
+class [[nodiscard]] StatusOr {
+ public:
+ // Constructs a new StatusOr with status::undefined status.
+ // This is marked 'explicit' to try to catch cases like 'return {};',
+ // where people think StatusOr<std::vector<int>> will be initialized
+ // with an empty vector, instead of a status::undefined.
+ explicit StatusOr() = default;
+
+ // Implicit copy constructor and construction from T.
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ StatusOr(Status status) : mStatus(std::move(status)) { assert(!isOk(mStatus)); }
+
+ // Implicit construction from T. It is convenient and sensible to be able
+ // to do 'return T()' when the return type is StatusOr<T>.
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ StatusOr(const T& value) : mStatus(status::ok), mValue(value) {}
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ StatusOr(T&& value) : mStatus(status::ok), mValue(std::move(value)) {}
+
+ // Move constructor ok (if T supports move)
+ StatusOr(StatusOr&&) noexcept = default;
+ // Move assignment ok (if T supports move)
+ StatusOr& operator=(StatusOr&&) noexcept = default;
+ // Copy constructor ok (if T supports copy)
+ StatusOr(const StatusOr&) = default;
+ // Copy assignment ok (if T supports copy)
+ StatusOr& operator=(const StatusOr&) = default;
+
+ // Returns a const reference to wrapped type.
+ // It is an error to call value() when !isOk(status())
+ const T& value() const & { return mValue; }
+ const T&& value() const && { return mValue; }
+
+ // Returns an rvalue reference to wrapped type
+ // It is an error to call value() when !isOk(status())
+ //
+ // If T is expensive to copy but supports efficient move, it can be moved
+ // out of a StatusOr as follows:
+ // T value = std::move(statusor).value();
+ T& value() & { return mValue; }
+ T&& value() && { return mValue; }
+
+ // Returns the Status object assigned at construction time.
+ const Status status() const { return mStatus; }
+
+ // Explicitly ignores the Status without triggering [[nodiscard]] errors.
+ void ignoreError() const {}
+
+ // Implicit cast to Status.
+ // NOLINTNEXTLINE(google-explicit-constructor)
+ operator Status() const { return status(); }
+
+ private:
+ Status mStatus = status::undefined;
+ T mValue;
+};
+
+template <typename T>
+inline std::ostream& operator<<(std::ostream& os, const StatusOr<T>& s) {
+ return os << "StatusOr[status: " << s.status() << "]";
+}
+
+#define ASSIGN_OR_RETURN_IMPL(tmp, lhs, stmt) \
+ auto tmp = (stmt); \
+ RETURN_IF_NOT_OK(tmp); \
+ lhs = std::move(tmp.value());
+
+#define ASSIGN_OR_RETURN_CONCAT(line, lhs, stmt) \
+ ASSIGN_OR_RETURN_IMPL(__CONCAT(_status_or_, line), lhs, stmt)
+
+// Macro to allow exception-like handling of error return values.
+//
+// If the evaluation of stmt results in an error, return that error
+// from the current function. Otherwise, assign the result to lhs.
+//
+// This macro supports both move and copy assignment operators. lhs
+// may be either a new local variable or an existing non-const
+// variable accessible in the current scope.
+//
+// Example usage:
+// StatusOr<MyType> foo() { ... }
+//
+// ASSIGN_OR_RETURN(auto myVar, foo());
+// ASSIGN_OR_RETURN(myExistingVar, foo());
+// ASSIGN_OR_RETURN(myMemberVar, foo());
+#define ASSIGN_OR_RETURN(lhs, stmt) ASSIGN_OR_RETURN_CONCAT(__LINE__, lhs, stmt)
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_STATUSOR_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Stopwatch.h b/staticlibs/netd/libnetdutils/include/netdutils/Stopwatch.h
new file mode 100644
index 0000000..e7b4326
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Stopwatch.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 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 NETDUTILS_STOPWATCH_H
+#define NETDUTILS_STOPWATCH_H
+
+#include <chrono>
+
+namespace android {
+namespace netdutils {
+
+class Stopwatch {
+ private:
+ using clock = std::chrono::steady_clock;
+ using time_point = std::chrono::time_point<clock>;
+
+ public:
+ Stopwatch() : mStart(clock::now()) {}
+ virtual ~Stopwatch() = default;
+
+ int64_t timeTakenUs() const { return getElapsedUs(clock::now()); }
+ int64_t getTimeAndResetUs() {
+ const auto& now = clock::now();
+ int64_t elapsed = getElapsedUs(now);
+ mStart = now;
+ return elapsed;
+ }
+
+ private:
+ time_point mStart;
+
+ int64_t getElapsedUs(const time_point& now) const {
+ return (std::chrono::duration_cast<std::chrono::microseconds>(now - mStart)).count();
+ }
+};
+
+} // namespace netdutils
+} // namespace android
+
+#endif // NETDUTILS_STOPWATCH_H
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Syscalls.h b/staticlibs/netd/libnetdutils/include/netdutils/Syscalls.h
new file mode 100644
index 0000000..36fcd85
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Syscalls.h
@@ -0,0 +1,204 @@
+/*
+ * 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 NETDUTILS_SYSCALLS_H
+#define NETDUTILS_SYSCALLS_H
+
+#include <memory>
+
+#include <net/if.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "netdutils/Fd.h"
+#include "netdutils/Slice.h"
+#include "netdutils/Socket.h"
+#include "netdutils/Status.h"
+#include "netdutils/StatusOr.h"
+#include "netdutils/UniqueFd.h"
+#include "netdutils/UniqueFile.h"
+
+namespace android {
+namespace netdutils {
+
+class Syscalls {
+ public:
+ virtual ~Syscalls() = default;
+
+ virtual StatusOr<UniqueFd> open(const std::string& pathname, int flags,
+ mode_t mode = 0) const = 0;
+
+ virtual StatusOr<UniqueFd> socket(int domain, int type, int protocol) const = 0;
+
+ virtual Status getsockname(Fd sock, sockaddr* addr, socklen_t* addrlen) const = 0;
+
+ virtual Status getsockopt(Fd sock, int level, int optname, void *optval,
+ socklen_t *optlen) const = 0;
+
+ virtual Status setsockopt(Fd sock, int level, int optname, const void* optval,
+ socklen_t optlen) const = 0;
+
+ virtual Status bind(Fd sock, const sockaddr* addr, socklen_t addrlen) const = 0;
+
+ virtual Status connect(Fd sock, const sockaddr* addr, socklen_t addrlen) const = 0;
+
+ virtual StatusOr<ifreq> ioctl(Fd sock, unsigned long request, ifreq* ifr) const = 0;
+
+ virtual StatusOr<UniqueFd> eventfd(unsigned int initval, int flags) const = 0;
+
+ virtual StatusOr<int> ppoll(pollfd* fds, nfds_t nfds, double timeout) const = 0;
+
+ virtual StatusOr<size_t> writev(Fd fd, const std::vector<iovec>& iov) const = 0;
+
+ virtual StatusOr<size_t> write(Fd fd, const Slice buf) const = 0;
+
+ virtual StatusOr<Slice> read(Fd fd, const Slice buf) const = 0;
+
+ virtual StatusOr<size_t> sendto(Fd sock, const Slice buf, int flags, const sockaddr* dst,
+ socklen_t dstlen) const = 0;
+
+ virtual StatusOr<Slice> recvfrom(Fd sock, const Slice dst, int flags, sockaddr* src,
+ socklen_t* srclen) const = 0;
+
+ virtual Status shutdown(Fd fd, int how) const = 0;
+
+ virtual Status close(Fd fd) const = 0;
+
+ virtual StatusOr<UniqueFile> fopen(const std::string& path, const std::string& mode) const = 0;
+
+ virtual StatusOr<int> vfprintf(FILE* file, const char* format, va_list ap) const = 0;
+
+ virtual StatusOr<int> vfscanf(FILE* file, const char* format, va_list ap) const = 0;
+
+ virtual Status fclose(FILE* file) const = 0;
+
+ virtual StatusOr<pid_t> fork() const = 0;
+
+ // va_args helpers
+ // va_start doesn't work when the preceding argument is a reference
+ // type so we're forced to use const char*.
+ StatusOr<int> fprintf(FILE* file, const char* format, ...) const {
+ va_list ap;
+ va_start(ap, format);
+ auto result = vfprintf(file, format, ap);
+ va_end(ap);
+ return result;
+ }
+
+ // va_start doesn't work when the preceding argument is a reference
+ // type so we're forced to use const char*.
+ StatusOr<int> fscanf(FILE* file, const char* format, ...) const {
+ va_list ap;
+ va_start(ap, format);
+ auto result = vfscanf(file, format, ap);
+ va_end(ap);
+ return result;
+ }
+
+ // Templated helpers that forward directly to methods declared above
+ template <typename SockaddrT>
+ StatusOr<SockaddrT> getsockname(Fd sock) const {
+ SockaddrT addr = {};
+ socklen_t addrlen = sizeof(addr);
+ RETURN_IF_NOT_OK(getsockname(sock, asSockaddrPtr(&addr), &addrlen));
+ return addr;
+ }
+
+ template <typename SockoptT>
+ Status getsockopt(Fd sock, int level, int optname, void* optval, socklen_t* optlen) const {
+ return getsockopt(sock, level, optname, optval, optlen);
+ }
+
+ template <typename SockoptT>
+ Status setsockopt(Fd sock, int level, int optname, const SockoptT& opt) const {
+ return setsockopt(sock, level, optname, &opt, sizeof(opt));
+ }
+
+ template <typename SockaddrT>
+ Status bind(Fd sock, const SockaddrT& addr) const {
+ return bind(sock, asSockaddrPtr(&addr), sizeof(addr));
+ }
+
+ template <typename SockaddrT>
+ Status connect(Fd sock, const SockaddrT& addr) const {
+ return connect(sock, asSockaddrPtr(&addr), sizeof(addr));
+ }
+
+ template <size_t size>
+ StatusOr<std::array<uint16_t, size>> ppoll(const std::array<Fd, size>& fds, uint16_t events,
+ double timeout) const {
+ std::array<pollfd, size> tmp;
+ for (size_t i = 0; i < size; ++i) {
+ tmp[i].fd = fds[i].get();
+ tmp[i].events = events;
+ tmp[i].revents = 0;
+ }
+ RETURN_IF_NOT_OK(ppoll(tmp.data(), tmp.size(), timeout).status());
+ std::array<uint16_t, size> out;
+ for (size_t i = 0; i < size; ++i) {
+ out[i] = tmp[i].revents;
+ }
+ return out;
+ }
+
+ template <typename SockaddrT>
+ StatusOr<size_t> sendto(Fd sock, const Slice buf, int flags, const SockaddrT& dst) const {
+ return sendto(sock, buf, flags, asSockaddrPtr(&dst), sizeof(dst));
+ }
+
+ // Ignore src sockaddr
+ StatusOr<Slice> recvfrom(Fd sock, const Slice dst, int flags) const {
+ return recvfrom(sock, dst, flags, nullptr, nullptr);
+ }
+
+ template <typename SockaddrT>
+ StatusOr<std::pair<Slice, SockaddrT>> recvfrom(Fd sock, const Slice dst, int flags) const {
+ SockaddrT addr = {};
+ socklen_t addrlen = sizeof(addr);
+ ASSIGN_OR_RETURN(auto used, recvfrom(sock, dst, flags, asSockaddrPtr(&addr), &addrlen));
+ return std::make_pair(used, addr);
+ }
+};
+
+// Specialized singleton that supports zero initialization and runtime
+// override of contained pointer.
+class SyscallsHolder {
+ public:
+ ~SyscallsHolder();
+
+ // Return a pointer to an unowned instance of Syscalls.
+ Syscalls& get();
+
+ // Testing only: set the value returned by getSyscalls. Return the old value.
+ // Callers are responsible for restoring the previous value returned
+ // by getSyscalls to avoid leaks.
+ Syscalls& swap(Syscalls& syscalls);
+
+ private:
+ std::atomic<Syscalls*> mSyscalls{nullptr};
+};
+
+// Syscalls instance used throughout netdutils
+extern SyscallsHolder sSyscalls;
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETDUTILS_SYSCALLS_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/ThreadUtil.h b/staticlibs/netd/libnetdutils/include/netdutils/ThreadUtil.h
new file mode 100644
index 0000000..62e6f70
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/ThreadUtil.h
@@ -0,0 +1,83 @@
+/*
+ * 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 NETDUTILS_THREADUTIL_H
+#define NETDUTILS_THREADUTIL_H
+
+#include <pthread.h>
+#include <memory>
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace netdutils {
+
+struct scoped_pthread_attr {
+ scoped_pthread_attr() { pthread_attr_init(&attr); }
+ ~scoped_pthread_attr() { pthread_attr_destroy(&attr); }
+
+ int detach() { return -pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); }
+
+ pthread_attr_t attr;
+};
+
+inline void setThreadName(std::string name) {
+ // MAX_TASK_COMM_LEN=16 is not exported by bionic.
+ const size_t MAX_TASK_COMM_LEN = 16;
+
+ // Crop name to 16 bytes including the NUL byte, as required by pthread_setname_np()
+ if (name.size() >= MAX_TASK_COMM_LEN) name.resize(MAX_TASK_COMM_LEN - 1);
+
+ if (int ret = pthread_setname_np(pthread_self(), name.c_str()); ret != 0) {
+ LOG(WARNING) << "Unable to set thread name to " << name << ": " << strerror(ret);
+ }
+}
+
+template <typename T>
+inline void* runAndDelete(void* obj) {
+ std::unique_ptr<T> handler(reinterpret_cast<T*>(obj));
+ setThreadName(handler->threadName().c_str());
+ handler->run();
+ return nullptr;
+}
+
+template <typename T>
+inline int threadLaunch(T* obj) {
+ if (obj == nullptr) {
+ return -EINVAL;
+ }
+
+ scoped_pthread_attr scoped_attr;
+
+ int rval = scoped_attr.detach();
+ if (rval != 0) {
+ return rval;
+ }
+
+ pthread_t thread;
+ rval = pthread_create(&thread, &scoped_attr.attr, &runAndDelete<T>, obj);
+ if (rval != 0) {
+ LOG(WARNING) << __func__ << ": pthread_create failed: " << rval;
+ return -rval;
+ }
+
+ return rval;
+}
+
+} // namespace netdutils
+} // namespace android
+
+#endif // NETDUTILS_THREADUTIL_H
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/UidConstants.h b/staticlibs/netd/libnetdutils/include/netdutils/UidConstants.h
new file mode 100644
index 0000000..42c1090
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/UidConstants.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2019 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 NETDUTILS_UID_CONSTANTS_H
+#define NETDUTILS_UID_CONSTANTS_H
+
+// These are used by both eBPF kernel programs and netd, we cannot put them in NetdConstant.h since
+// we have to minimize the number of headers included by the BPF kernel program.
+#define MIN_SYSTEM_UID 0
+#define MAX_SYSTEM_UID 9999
+
+#define PER_USER_RANGE 100000
+
+#endif // NETDUTILS_UID_CONSTANTS_H
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/UniqueFd.h b/staticlibs/netd/libnetdutils/include/netdutils/UniqueFd.h
new file mode 100644
index 0000000..61101f9
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/UniqueFd.h
@@ -0,0 +1,71 @@
+/*
+ * 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 NETUTILS_UNIQUEFD_H
+#define NETUTILS_UNIQUEFD_H
+
+#include <unistd.h>
+#include <ostream>
+
+#include "netdutils/Fd.h"
+
+namespace android {
+namespace netdutils {
+
+// Stricter unique_fd implementation that:
+// *) Does not implement release()
+// *) Does not implicitly cast to int
+// *) Uses a strongly typed wrapper (Fd) for the underlying file descriptor
+//
+// Users of UniqueFd should endeavor to treat this as a completely
+// opaque object. The only code that should interpret the wrapped
+// value is in Syscalls.h
+class UniqueFd {
+ public:
+ UniqueFd() = default;
+
+ UniqueFd(Fd fd) : mFd(fd) {}
+
+ ~UniqueFd() { reset(); }
+
+ // Disallow copy
+ UniqueFd(const UniqueFd&) = delete;
+ UniqueFd& operator=(const UniqueFd&) = delete;
+
+ // Allow move
+ UniqueFd(UniqueFd&& other) { std::swap(mFd, other.mFd); }
+ UniqueFd& operator=(UniqueFd&& other) {
+ std::swap(mFd, other.mFd);
+ return *this;
+ }
+
+ // Cleanup any currently owned Fd, replacing it with the optional
+ // parameter fd
+ void reset(Fd fd = Fd());
+
+ // Implict cast to Fd
+ operator const Fd &() const { return mFd; }
+
+ private:
+ Fd mFd;
+};
+
+std::ostream& operator<<(std::ostream& os, const UniqueFd& fd);
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_UNIQUEFD_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/UniqueFile.h b/staticlibs/netd/libnetdutils/include/netdutils/UniqueFile.h
new file mode 100644
index 0000000..6dd6d67
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/UniqueFile.h
@@ -0,0 +1,35 @@
+/*
+ * 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 NETDUTILS_UNIQUEFILE_H
+#define NETDUTILS_UNIQUEFILE_H
+
+#include <stdio.h>
+#include <memory>
+
+namespace android {
+namespace netdutils {
+
+struct UniqueFileDtor {
+ void operator()(FILE* file) const;
+};
+
+using UniqueFile = std::unique_ptr<FILE, UniqueFileDtor>;
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETDUTILS_UNIQUEFILE_H */
diff --git a/staticlibs/netd/libnetdutils/include/netdutils/Utils.h b/staticlibs/netd/libnetdutils/include/netdutils/Utils.h
new file mode 100644
index 0000000..83c583b
--- /dev/null
+++ b/staticlibs/netd/libnetdutils/include/netdutils/Utils.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 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 NETUTILS_UTILS_H
+#define NETUTILS_UTILS_H
+
+#include "netdutils/StatusOr.h"
+
+namespace android {
+namespace netdutils {
+
+StatusOr<std::vector<std::string>> getIfaceNames();
+
+StatusOr<std::map<std::string, uint32_t>> getIfaceList();
+
+} // namespace netdutils
+} // namespace android
+
+#endif /* NETUTILS_UTILS_H */
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
index 0067931..649b30e 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTest.kt
@@ -32,6 +32,7 @@
class CleanupTest {
class TestException1 : Exception()
class TestException2 : Exception()
+ class TestException3 : Exception()
@Test
fun testNotThrow() {
@@ -172,4 +173,32 @@
assertTrue(x == 4)
assertTrue(thrown.suppressedExceptions.isEmpty())
}
+
+ @Test
+ fun testMultipleCleanups() {
+ var x = 1
+ val thrown = assertFailsWith<TestException1> {
+ tryTest {
+ x = 2
+ throw TestException1()
+ } cleanupStep {
+ assertTrue(x == 2)
+ x = 3
+ throw TestException2()
+ x = 4
+ } cleanupStep {
+ assertTrue(x == 3)
+ x = 5
+ throw TestException3()
+ x = 6
+ } cleanup {
+ assertTrue(x == 5)
+ x = 7
+ }
+ }
+ assertEquals(2, thrown.suppressedExceptions.size)
+ assertTrue(thrown.suppressedExceptions[0] is TestException2)
+ assertTrue(thrown.suppressedExceptions[1] is TestException3)
+ assert(x == 7)
+ }
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
index 83abfa1..8a13397 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/CleanupTestJava.java
@@ -20,6 +20,7 @@
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import android.util.Log;
@@ -31,6 +32,7 @@
private static final String TAG = CleanupTestJava.class.getSimpleName();
private static final class TestException1 extends Exception {}
private static final class TestException2 extends Exception {}
+ private static final class TestException3 extends Exception {}
@Test
public void testNotThrow() {
@@ -93,4 +95,27 @@
);
assertEquals(3, x.get());
}
+
+ @Test
+ public void testMultipleCleanups() {
+ final AtomicInteger x = new AtomicInteger(1);
+ final TestException1 exception = assertThrows(TestException1.class, () ->
+ testAndCleanup(() -> {
+ x.compareAndSet(1, 2);
+ throw new TestException1();
+ }, () -> {
+ x.compareAndSet(2, 3);
+ throw new TestException2();
+ }, () -> {
+ x.compareAndSet(3, 4);
+ throw new TestException3();
+ }, () -> {
+ x.compareAndSet(4, 5);
+ })
+ );
+ assertEquals(2, exception.getSuppressed().length);
+ assertTrue(exception.getSuppressed()[0] instanceof TestException2);
+ assertTrue(exception.getSuppressed()[1] instanceof TestException3);
+ assertEquals(5, x.get());
+ }
}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java
new file mode 100644
index 0000000..48777ac
--- /dev/null
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/DnsPacketUtilsTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+package com.android.net.module.util;
+
+import static com.android.net.module.util.DnsPacketUtils.DnsRecordParser;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.ByteBuffer;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsPacketUtilsTest {
+
+ /**
+ * Verifies that the compressed NAME field in the answer section of the DNS message is parsed
+ * successfully when name compression is permitted. Additionally, verifies that a
+ * {@link DnsPacket.ParseException} is thrown in a hypothetical scenario where name compression
+ * is not expected.
+ */
+ @Test
+ public void testParsingAnswerSectionNameCompressed() throws Exception {
+ final byte[] v4blobNameCompressedAnswer = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ (byte) 0xc0, 0x0c, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ final int answerOffsetBytePosition = 32;
+ final ByteBuffer nameCompressedBuf = ByteBuffer.wrap(v4blobNameCompressedAnswer);
+
+ nameCompressedBuf.position(answerOffsetBytePosition);
+ assertThrows(DnsPacket.ParseException.class, () -> DnsRecordParser.parseName(
+ nameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */false));
+
+ nameCompressedBuf.position(answerOffsetBytePosition);
+ String domainName = DnsRecordParser.parseName(
+ nameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */true);
+ assertEquals(domainName, "www.google.com");
+ }
+
+ /**
+ * Verifies that an uncompressed NAME field in the answer section of the DNS message is parsed
+ * successfully irrespective of whether name compression is permitted.
+ */
+ @Test
+ public void testParsingAnswerSectionNoNameCompression() throws Exception {
+ final byte[] v4blobNoNameCompression = new byte[] {
+ /* Header */
+ 0x55, 0x66, /* Transaction ID */
+ (byte) 0x81, (byte) 0x80, /* Flags */
+ 0x00, 0x01, /* Questions */
+ 0x00, 0x01, /* Answer RRs */
+ 0x00, 0x00, /* Authority RRs */
+ 0x00, 0x00, /* Additional RRs */
+ /* Queries */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ /* Answers */
+ 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+ 0x00, 0x01, /* Type */
+ 0x00, 0x01, /* Class */
+ 0x00, 0x00, 0x01, 0x2b, /* TTL */
+ 0x00, 0x04, /* Data length */
+ (byte) 0xac, (byte) 0xd9, (byte) 0xa1, (byte) 0x84 /* Address */
+ };
+ final int answerOffsetBytePosition = 32;
+ final ByteBuffer notNameCompressedBuf = ByteBuffer.wrap(v4blobNoNameCompression);
+
+ notNameCompressedBuf.position(answerOffsetBytePosition);
+ String domainName = DnsRecordParser.parseName(
+ notNameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */ true);
+ assertEquals(domainName, "www.google.com");
+
+ notNameCompressedBuf.position(answerOffsetBytePosition);
+ domainName = DnsRecordParser.parseName(
+ notNameCompressedBuf, /* depth= */ 0, /* isNameCompressionSupported= */ false);
+ assertEquals(domainName, "www.google.com");
+ }
+}
diff --git a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
index 5d446b8..9db63db 100644
--- a/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
+++ b/staticlibs/tests/unit/src/com/android/net/module/util/netlink/RtNetlinkLinkMessageTest.java
@@ -47,7 +47,7 @@
private static final String RTM_NEWLINK_HEX =
"64000000100000000000000000000000" // struct nlmsghr
+ "000001001E0000000210000000000000" // struct ifinfo
- + "0A000300776C616E30000000" // IFLA_IFNAME
+ + "0A000300776C616E30000000" // IFLA_IFNAME(wlan0)
+ "08000D00B80B0000" // IFLA_PROTINFO
+ "0500100002000000" // IFLA_OPERSTATE
+ "0500110001000000" // IFLA_LINKMODE
@@ -88,12 +88,47 @@
assertTrue(linkMsg.getInterfaceName().equals("wlan0"));
}
+ /**
+ * Example:
+ * # adb shell ip tunnel add トン0 mode sit local any remote 8.8.8.8
+ * # adb shell ip link show | grep トン
+ * 33: トン0@NONE: <POINTOPOINT,NOARP> mtu 1480 qdisc noop state DOWN mode DEFAULT group
+ * default qlen 1000
+ *
+ * IFLA_IFNAME attribute: \x0c\x00\x03\x00\xe3\x83\x88\xe3\x83\xb3\x30\x00
+ * length: 0x000c
+ * type: 0x0003
+ * value: \xe3\x83\x88\xe3\x83\xb3\x30\x00
+ * ト (\xe3\x83\x88)
+ * ン (\xe3\x83\xb3)
+ * 0 (\x30)
+ * null terminated (\x00)
+ */
+ private static final String RTM_NEWLINK_UTF8_HEX =
+ "34000000100000000000000000000000" // struct nlmsghr
+ + "000001001E0000000210000000000000" // struct ifinfo
+ + "08000400DC050000" // IFLA_MTU
+ + "0A00010092C3E3C9374E0000" // IFLA_ADDRESS
+ + "0C000300E38388E383B33000"; // IFLA_IFNAME(トン0)
+
+ @Test
+ public void testParseRtmNewLink_utf8Ifname() {
+ final ByteBuffer byteBuffer = toByteBuffer(RTM_NEWLINK_UTF8_HEX);
+ byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
+ final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer, NETLINK_ROUTE);
+ assertNotNull(msg);
+ assertTrue(msg instanceof RtNetlinkLinkMessage);
+ final RtNetlinkLinkMessage linkMsg = (RtNetlinkLinkMessage) msg;
+
+ assertTrue(linkMsg.getInterfaceName().equals("トン0"));
+ }
+
private static final String RTM_NEWLINK_PACK_HEX =
"34000000100000000000000000000000" // struct nlmsghr
+ "000001001E0000000210000000000000" // struct ifinfo
+ "08000400DC050000" // IFLA_MTU
+ "0A00010092C3E3C9374E0000" // IFLA_ADDRESS
- + "0A000300776C616E30000000"; // IFLA_IFNAME
+ + "0A000300776C616E30000000"; // IFLA_IFNAME(wlan0)
@Test
public void testPackRtmNewLink() {
@@ -117,7 +152,7 @@
+ "0500100002000000" // IFLA_OPERSTATE
+ "0800010092C3E3C9" // IFLA_ADDRESS(truncated)
+ "0500110001000000" // IFLA_LINKMODE
- + "0A000300776C616E30000000" // IFLA_IFNAME
+ + "0A000300776C616E30000000" // IFLA_IFNAME(wlan0)
+ "08000400DC050000"; // IFLA_MTU
@Test
diff --git a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt
index be5c9b2..4a7b351 100644
--- a/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt
+++ b/staticlibs/testutils/devicetests/com/android/testutils/TestableNetworkStatsProvider.kt
@@ -17,6 +17,7 @@
package com.android.testutils
import android.net.netstats.provider.NetworkStatsProvider
+import android.util.Log
import com.android.net.module.util.ArrayTrackRecord
import kotlin.test.assertEquals
import kotlin.test.assertTrue
@@ -43,23 +44,28 @@
data class OnSetAlert(val quotaBytes: Long) : CallbackType()
}
+ private val TAG = this::class.simpleName
val history = ArrayTrackRecord<CallbackType>().newReadHead()
// See ReadHead#mark
val mark get() = history.mark
override fun onRequestStatsUpdate(token: Int) {
+ Log.d(TAG, "onRequestStatsUpdate $token")
history.add(CallbackType.OnRequestStatsUpdate(token))
}
override fun onSetWarningAndLimit(iface: String, warningBytes: Long, limitBytes: Long) {
+ Log.d(TAG, "onSetWarningAndLimit $iface $warningBytes $limitBytes")
history.add(CallbackType.OnSetWarningAndLimit(iface, warningBytes, limitBytes))
}
override fun onSetLimit(iface: String, quotaBytes: Long) {
+ Log.d(TAG, "onSetLimit $iface $quotaBytes")
history.add(CallbackType.OnSetLimit(iface, quotaBytes))
}
override fun onSetAlert(quotaBytes: Long) {
+ Log.d(TAG, "onSetAlert $quotaBytes")
history.add(CallbackType.OnSetAlert(quotaBytes))
}
diff --git a/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt b/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
index 1b67f68..45783d8 100644
--- a/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
+++ b/staticlibs/testutils/hostdevice/com/android/testutils/Cleanup.kt
@@ -22,14 +22,6 @@
import com.android.testutils.ExceptionUtils.ThrowingSupplier
import javax.annotation.CheckReturnValue
-@CheckReturnValue
-fun <T> tryTest(block: () -> T) = TryExpr(
- try {
- Result.success(block())
- } catch (e: Throwable) {
- Result.failure(e)
- })
-
/**
* Utility to do cleanup in tests without replacing exceptions with those from a finally block.
*
@@ -54,11 +46,15 @@
* to the standard try{}finally{}, if both throws, the construct throws the exception that happened
* in tryTest{} rather than the one that happened in cleanup{}.
*
- * Kotlin usage is as try{}finally{} :
+ * Kotlin usage is as try{}finally{}, but with multiple finally{} blocks :
* tryTest {
* testing code
+ * } cleanupStep {
+ * cleanup code 1
+ * } cleanupStep {
+ * cleanup code 2
* } cleanup {
- * cleanup code
+ * cleanup code 3
* }
* Catch blocks can be added with the following syntax :
* tryTest {
@@ -67,14 +63,24 @@
* do something to it
* }
*
- * Java doesn't allow this kind of syntax, so instead a function taking 2 lambdas is provided.
+ * Java doesn't allow this kind of syntax, so instead a function taking lambdas is provided.
* testAndCleanup(() -> {
* testing code
* }, () -> {
- * cleanup code
+ * cleanup code 1
+ * }, () -> {
+ * cleanup code 2
* });
*/
+@CheckReturnValue
+fun <T> tryTest(block: () -> T) = TryExpr(
+ try {
+ Result.success(block())
+ } catch (e: Throwable) {
+ Result.failure(e)
+ })
+
// Some downstream branches have an older kotlin that doesn't know about value classes.
// TODO : Change this to "value class" when aosp no longer merges into such branches.
@Suppress("INLINE_CLASS_DEPRECATED")
@@ -89,30 +95,31 @@
})
}
- inline infix fun cleanup(block: () -> Unit): T {
+ @CheckReturnValue
+ inline infix fun cleanupStep(block: () -> Unit): TryExpr<T> {
try {
block()
} catch (e: Throwable) {
val originalException = result.exceptionOrNull()
- if (null == originalException) {
- throw e
+ return TryExpr(if (null == originalException) {
+ Result.failure(e)
} else {
originalException.addSuppressed(e)
- throw originalException
- }
+ Result.failure(originalException)
+ })
}
- return result.getOrThrow()
+ return this
}
+
+ inline infix fun cleanup(block: () -> Unit): T = cleanupStep(block).result.getOrThrow()
}
// Java support
-fun <T> testAndCleanup(tryBlock: ThrowingSupplier<T>, cleanupBlock: ThrowingRunnable): T {
- return tryTest {
- tryBlock.get()
- } cleanup {
- cleanupBlock.run()
- }
+fun <T> testAndCleanup(tryBlock: ThrowingSupplier<T>, vararg cleanupBlock: ThrowingRunnable): T {
+ return cleanupBlock.fold(tryTest { tryBlock.get() }) { previousExpr, nextCleanup ->
+ previousExpr.cleanupStep { nextCleanup.run() }
+ }.cleanup {}
}
-fun testAndCleanup(tryBlock: ThrowingRunnable, cleanupBlock: ThrowingRunnable) {
- return testAndCleanup(ThrowingSupplier { tryBlock.run() }, cleanupBlock)
+fun testAndCleanup(tryBlock: ThrowingRunnable, vararg cleanupBlock: ThrowingRunnable) {
+ return testAndCleanup(ThrowingSupplier { tryBlock.run() }, *cleanupBlock)
}