summaryrefslogtreecommitdiff
path: root/common/device/com/android/net/module/util
diff options
context:
space:
mode:
authorXiao Ma <xiaom@google.com>2021-07-01 13:57:02 +0000
committerXiao Ma <xiaom@google.com>2021-07-29 03:52:09 +0000
commita5abbe652945803803a0fe1985a7cb9712392302 (patch)
tree66172ef37e240fb91aa7c93a525b155688f46eea /common/device/com/android/net/module/util
parent5d39cb6ea74958c60b72e81ea5052e8a82928177 (diff)
downloadnet-a5abbe652945803803a0fe1985a7cb9712392302.tar.gz
Migrate netlink-client to net-utils-device-common-netlink.
Move netlink stuff to frameworks/libs/net/common/device, and build the source files as an individual libraray. NetworkStack module just depends on the net-utils-device-common-netlink. Besides, also fix the incorrect format detected by checkstyle_hook script such as missing java doc and make some public function as private, rename the variable and etc. Bug: 192535368 Test: atest NetworkStaticlibTests Change-Id: I00e7f30be1bc9ebc2e24d7cd53efc403d6ba3daa
Diffstat (limited to 'common/device/com/android/net/module/util')
-rw-r--r--common/device/com/android/net/module/util/HexDump.java2
-rw-r--r--common/device/com/android/net/module/util/Struct.java5
-rw-r--r--common/device/com/android/net/module/util/netlink/ConntrackMessage.java558
-rw-r--r--common/device/com/android/net/module/util/netlink/InetDiagMessage.java231
-rw-r--r--common/device/com/android/net/module/util/netlink/NdOption.java83
-rw-r--r--common/device/com/android/net/module/util/netlink/NduseroptMessage.java141
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkConstants.java260
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java64
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkMessage.java153
-rw-r--r--common/device/com/android/net/module/util/netlink/NetlinkSocket.java174
-rw-r--r--common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java237
-rw-r--r--common/device/com/android/net/module/util/netlink/StructInetDiagMsg.java62
-rw-r--r--common/device/com/android/net/module/util/netlink/StructInetDiagReqV2.java100
-rw-r--r--common/device/com/android/net/module/util/netlink/StructInetDiagSockId.java90
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNdMsg.java198
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNdOptPref64.java169
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNdaCacheInfo.java133
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNfGenMsg.java105
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNlAttr.java315
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNlMsgErr.java77
-rw-r--r--common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java173
-rw-r--r--common/device/com/android/net/module/util/structs/PrefixInformationOption.java3
22 files changed, 3329 insertions, 4 deletions
diff --git a/common/device/com/android/net/module/util/HexDump.java b/common/device/com/android/net/module/util/HexDump.java
index 6d36487b..a27c0a3c 100644
--- a/common/device/com/android/net/module/util/HexDump.java
+++ b/common/device/com/android/net/module/util/HexDump.java
@@ -16,7 +16,7 @@
package com.android.net.module.util;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
/**
* Hex utility functions.
diff --git a/common/device/com/android/net/module/util/Struct.java b/common/device/com/android/net/module/util/Struct.java
index d95c3037..b43e2c45 100644
--- a/common/device/com/android/net/module/util/Struct.java
+++ b/common/device/com/android/net/module/util/Struct.java
@@ -16,10 +16,11 @@
package com.android.net.module.util;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.net.MacAddress;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
diff --git a/common/device/com/android/net/module/util/netlink/ConntrackMessage.java b/common/device/com/android/net/module/util/netlink/ConntrackMessage.java
new file mode 100644
index 00000000..1763c04b
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/ConntrackMessage.java
@@ -0,0 +1,558 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.netlink;
+
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+import static com.android.net.module.util.netlink.StructNlAttr.findNextAttrOfType;
+import static com.android.net.module.util.netlink.StructNlAttr.makeNestedType;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+/**
+ * A NetlinkMessage subclass for netlink conntrack messages.
+ *
+ * see also: &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+ *
+ * @hide
+ */
+public class ConntrackMessage extends NetlinkMessage {
+ public static final int STRUCT_SIZE = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
+
+ // enum ctattr_type
+ public static final short CTA_TUPLE_ORIG = 1;
+ public static final short CTA_TUPLE_REPLY = 2;
+ public static final short CTA_STATUS = 3;
+ public static final short CTA_TIMEOUT = 7;
+
+ // enum ctattr_tuple
+ public static final short CTA_TUPLE_IP = 1;
+ public static final short CTA_TUPLE_PROTO = 2;
+
+ // enum ctattr_ip
+ public static final short CTA_IP_V4_SRC = 1;
+ public static final short CTA_IP_V4_DST = 2;
+
+ // enum ctattr_l4proto
+ public static final short CTA_PROTO_NUM = 1;
+ public static final short CTA_PROTO_SRC_PORT = 2;
+ public static final short CTA_PROTO_DST_PORT = 3;
+
+ // enum ip_conntrack_status
+ public static final int IPS_EXPECTED = 0x00000001;
+ public static final int IPS_SEEN_REPLY = 0x00000002;
+ public static final int IPS_ASSURED = 0x00000004;
+ public static final int IPS_CONFIRMED = 0x00000008;
+ public static final int IPS_SRC_NAT = 0x00000010;
+ public static final int IPS_DST_NAT = 0x00000020;
+ public static final int IPS_SEQ_ADJUST = 0x00000040;
+ public static final int IPS_SRC_NAT_DONE = 0x00000080;
+ public static final int IPS_DST_NAT_DONE = 0x00000100;
+ public static final int IPS_DYING = 0x00000200;
+ public static final int IPS_FIXED_TIMEOUT = 0x00000400;
+ public static final int IPS_TEMPLATE = 0x00000800;
+ public static final int IPS_UNTRACKED = 0x00001000;
+ public static final int IPS_HELPER = 0x00002000;
+ public static final int IPS_OFFLOAD = 0x00004000;
+ public static final int IPS_HW_OFFLOAD = 0x00008000;
+
+ // ip_conntrack_status mask
+ // Interesting on the NAT conntrack session which has already seen two direction traffic.
+ // TODO: Probably IPS_{SRC, DST}_NAT_DONE are also interesting.
+ public static final int ESTABLISHED_MASK = IPS_CONFIRMED | IPS_ASSURED | IPS_SEEN_REPLY
+ | IPS_SRC_NAT;
+ // Interesting on the established NAT conntrack session which is dying.
+ public static final int DYING_MASK = ESTABLISHED_MASK | IPS_DYING;
+
+ /**
+ * A tuple for the conntrack connection information.
+ *
+ * see also CTA_TUPLE_ORIG and CTA_TUPLE_REPLY.
+ */
+ public static class Tuple {
+ public final Inet4Address srcIp;
+ public final Inet4Address dstIp;
+
+ // Both port and protocol number are unsigned numbers stored in signed integers, and that
+ // callers that want to compare them to integers should either cast those integers, or
+ // convert them to unsigned using Byte.toUnsignedInt() and Short.toUnsignedInt().
+ public final short srcPort;
+ public final short dstPort;
+ public final byte protoNum;
+
+ public Tuple(TupleIpv4 ip, TupleProto proto) {
+ this.srcIp = ip.src;
+ this.dstIp = ip.dst;
+ this.srcPort = proto.srcPort;
+ this.dstPort = proto.dstPort;
+ this.protoNum = proto.protoNum;
+ }
+
+ @Override
+ @VisibleForTesting
+ public boolean equals(Object o) {
+ if (!(o instanceof Tuple)) return false;
+ Tuple that = (Tuple) o;
+ return Objects.equals(this.srcIp, that.srcIp)
+ && Objects.equals(this.dstIp, that.dstIp)
+ && this.srcPort == that.srcPort
+ && this.dstPort == that.dstPort
+ && this.protoNum == that.protoNum;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(srcIp, dstIp, srcPort, dstPort, protoNum);
+ }
+
+ @Override
+ public String toString() {
+ final String srcIpStr = (srcIp == null) ? "null" : srcIp.getHostAddress();
+ final String dstIpStr = (dstIp == null) ? "null" : dstIp.getHostAddress();
+ final String protoStr = NetlinkConstants.stringForProtocol(protoNum);
+
+ return "Tuple{"
+ + protoStr + ": "
+ + srcIpStr + ":" + Short.toUnsignedInt(srcPort) + " -> "
+ + dstIpStr + ":" + Short.toUnsignedInt(dstPort)
+ + "}";
+ }
+ }
+
+ /**
+ * A tuple for the conntrack connection address.
+ *
+ * see also CTA_TUPLE_IP.
+ */
+ public static class TupleIpv4 {
+ public final Inet4Address src;
+ public final Inet4Address dst;
+
+ public TupleIpv4(Inet4Address src, Inet4Address dst) {
+ this.src = src;
+ this.dst = dst;
+ }
+ }
+
+ /**
+ * A tuple for the conntrack connection protocol.
+ *
+ * see also CTA_TUPLE_PROTO.
+ */
+ public static class TupleProto {
+ public final byte protoNum;
+ public final short srcPort;
+ public final short dstPort;
+
+ public TupleProto(byte protoNum, short srcPort, short dstPort) {
+ this.protoNum = protoNum;
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ }
+ }
+
+ /**
+ * Create a netlink message to refresh IPv4 conntrack entry timeout.
+ */
+ public static byte[] newIPv4TimeoutUpdateRequest(
+ int proto, Inet4Address src, int sport, Inet4Address dst, int dport, int timeoutSec) {
+ // *** STYLE WARNING ***
+ //
+ // Code below this point uses extra block indentation to highlight the
+ // packing of nested tuple netlink attribute types.
+ final StructNlAttr ctaTupleOrig = new StructNlAttr(CTA_TUPLE_ORIG,
+ new StructNlAttr(CTA_TUPLE_IP,
+ new StructNlAttr(CTA_IP_V4_SRC, src),
+ new StructNlAttr(CTA_IP_V4_DST, dst)),
+ new StructNlAttr(CTA_TUPLE_PROTO,
+ new StructNlAttr(CTA_PROTO_NUM, (byte) proto),
+ new StructNlAttr(CTA_PROTO_SRC_PORT, (short) sport, BIG_ENDIAN),
+ new StructNlAttr(CTA_PROTO_DST_PORT, (short) dport, BIG_ENDIAN)));
+
+ final StructNlAttr ctaTimeout = new StructNlAttr(CTA_TIMEOUT, timeoutSec, BIG_ENDIAN);
+
+ final int payloadLength = ctaTupleOrig.getAlignedLength() + ctaTimeout.getAlignedLength();
+ final byte[] bytes = new byte[STRUCT_SIZE + payloadLength];
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ final ConntrackMessage ctmsg = new ConntrackMessage();
+ ctmsg.mHeader.nlmsg_len = bytes.length;
+ ctmsg.mHeader.nlmsg_type = (NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8)
+ | NetlinkConstants.IPCTNL_MSG_CT_NEW;
+ ctmsg.mHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
+ ctmsg.mHeader.nlmsg_seq = 1;
+ ctmsg.pack(byteBuffer);
+
+ ctaTupleOrig.pack(byteBuffer);
+ ctaTimeout.pack(byteBuffer);
+
+ return bytes;
+ }
+
+ /**
+ * Parses a netfilter conntrack message from a {@link ByteBuffer}.
+ *
+ * @param header the netlink message header.
+ * @param byteBuffer The buffer from which to parse the netfilter conntrack message.
+ * @return the parsed netfilter conntrack message, or {@code null} if the netfilter conntrack
+ * message could not be parsed successfully (for example, if it was truncated).
+ */
+ public static ConntrackMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+ // Just build the netlink header and netfilter header for now and pretend the whole message
+ // was consumed.
+ // TODO: Parse the conntrack attributes.
+ final StructNfGenMsg nfGenMsg = StructNfGenMsg.parse(byteBuffer);
+ if (nfGenMsg == null) {
+ return null;
+ }
+
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = findNextAttrOfType(CTA_STATUS, byteBuffer);
+ int status = 0;
+ if (nlAttr != null) {
+ status = nlAttr.getValueAsBe32(0);
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(CTA_TIMEOUT, byteBuffer);
+ int timeoutSec = 0;
+ if (nlAttr != null) {
+ timeoutSec = nlAttr.getValueAsBe32(0);
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_ORIG), byteBuffer);
+ Tuple tupleOrig = null;
+ if (nlAttr != null) {
+ tupleOrig = parseTuple(nlAttr.getValueAsByteBuffer());
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_REPLY), byteBuffer);
+ Tuple tupleReply = null;
+ if (nlAttr != null) {
+ tupleReply = parseTuple(nlAttr.getValueAsByteBuffer());
+ }
+
+ // Advance to the end of the message.
+ byteBuffer.position(baseOffset);
+ final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
+ final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
+ header.nlmsg_len - kMinConsumed);
+ if (byteBuffer.remaining() < kAdditionalSpace) {
+ return null;
+ }
+ byteBuffer.position(baseOffset + kAdditionalSpace);
+
+ return new ConntrackMessage(header, nfGenMsg, tupleOrig, tupleReply, status, timeoutSec);
+ }
+
+ /**
+ * Parses a conntrack tuple from a {@link ByteBuffer}.
+ *
+ * The attribute parsing is interesting on:
+ * - CTA_TUPLE_IP
+ * CTA_IP_V4_SRC
+ * CTA_IP_V4_DST
+ * - CTA_TUPLE_PROTO
+ * CTA_PROTO_NUM
+ * CTA_PROTO_SRC_PORT
+ * CTA_PROTO_DST_PORT
+ *
+ * Assume that the minimum size is the sum of CTA_TUPLE_IP (size: 20) and CTA_TUPLE_PROTO
+ * (size: 28). Here is an example for an expected CTA_TUPLE_ORIG message in raw data:
+ * +--------------------------------------------------------------------------------------+
+ * | CTA_TUPLE_ORIG |
+ * +--------------------------+-----------------------------------------------------------+
+ * | 1400 | nla_len = 20 |
+ * | 0180 | nla_type = nested CTA_TUPLE_IP |
+ * | 0800 0100 C0A8500C | nla_type=CTA_IP_V4_SRC, ip=192.168.80.12 |
+ * | 0800 0200 8C700874 | nla_type=CTA_IP_V4_DST, ip=140.112.8.116 |
+ * | 1C00 | nla_len = 28 |
+ * | 0280 | nla_type = nested CTA_TUPLE_PROTO |
+ * | 0500 0100 06 000000 | nla_type=CTA_PROTO_NUM, proto=IPPROTO_TCP (6) |
+ * | 0600 0200 F3F1 0000 | nla_type=CTA_PROTO_SRC_PORT, port=62449 (big endian) |
+ * | 0600 0300 01BB 0000 | nla_type=CTA_PROTO_DST_PORT, port=433 (big endian) |
+ * +--------------------------+-----------------------------------------------------------+
+ *
+ * The position of the byte buffer doesn't set to the end when the function returns. It is okay
+ * because the caller ConntrackMessage#parse has passed a copy which is used for this parser
+ * only. Moreover, the parser behavior is the same as other existing netlink struct class
+ * parser. Ex: StructInetDiagMsg#parse.
+ */
+ @Nullable
+ private static Tuple parseTuple(@Nullable ByteBuffer byteBuffer) {
+ if (byteBuffer == null) return null;
+
+ TupleIpv4 tupleIpv4 = null;
+ TupleProto tupleProto = null;
+
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_IP), byteBuffer);
+ if (nlAttr != null) {
+ tupleIpv4 = parseTupleIpv4(nlAttr.getValueAsByteBuffer());
+ }
+ if (tupleIpv4 == null) return null;
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(makeNestedType(CTA_TUPLE_PROTO), byteBuffer);
+ if (nlAttr != null) {
+ tupleProto = parseTupleProto(nlAttr.getValueAsByteBuffer());
+ }
+ if (tupleProto == null) return null;
+
+ return new Tuple(tupleIpv4, tupleProto);
+ }
+
+ @Nullable
+ private static Inet4Address castToInet4Address(@Nullable InetAddress address) {
+ if (address == null || !(address instanceof Inet4Address)) return null;
+ return (Inet4Address) address;
+ }
+
+ @Nullable
+ private static TupleIpv4 parseTupleIpv4(@Nullable ByteBuffer byteBuffer) {
+ if (byteBuffer == null) return null;
+
+ Inet4Address src = null;
+ Inet4Address dst = null;
+
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = findNextAttrOfType(CTA_IP_V4_SRC, byteBuffer);
+ if (nlAttr != null) {
+ src = castToInet4Address(nlAttr.getValueAsInetAddress());
+ }
+ if (src == null) return null;
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(CTA_IP_V4_DST, byteBuffer);
+ if (nlAttr != null) {
+ dst = castToInet4Address(nlAttr.getValueAsInetAddress());
+ }
+ if (dst == null) return null;
+
+ return new TupleIpv4(src, dst);
+ }
+
+ @Nullable
+ private static TupleProto parseTupleProto(@Nullable ByteBuffer byteBuffer) {
+ if (byteBuffer == null) return null;
+
+ byte protoNum = 0;
+ short srcPort = 0;
+ short dstPort = 0;
+
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = findNextAttrOfType(CTA_PROTO_NUM, byteBuffer);
+ if (nlAttr != null) {
+ protoNum = nlAttr.getValueAsByte((byte) 0);
+ }
+ if (!(protoNum == IPPROTO_TCP || protoNum == IPPROTO_UDP)) return null;
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(CTA_PROTO_SRC_PORT, byteBuffer);
+ if (nlAttr != null) {
+ srcPort = nlAttr.getValueAsBe16((short) 0);
+ }
+ if (srcPort == 0) return null;
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(CTA_PROTO_DST_PORT, byteBuffer);
+ if (nlAttr != null) {
+ dstPort = nlAttr.getValueAsBe16((short) 0);
+ }
+ if (dstPort == 0) return null;
+
+ return new TupleProto(protoNum, srcPort, dstPort);
+ }
+
+ /**
+ * Netfilter header.
+ */
+ public final StructNfGenMsg nfGenMsg;
+ /**
+ * Original direction conntrack tuple.
+ *
+ * The tuple is determined by the parsed attribute value CTA_TUPLE_ORIG, or null if the
+ * tuple could not be parsed successfully (for example, if it was truncated or absent).
+ */
+ @Nullable
+ public final Tuple tupleOrig;
+ /**
+ * Reply direction conntrack tuple.
+ *
+ * The tuple is determined by the parsed attribute value CTA_TUPLE_REPLY, or null if the
+ * tuple could not be parsed successfully (for example, if it was truncated or absent).
+ */
+ @Nullable
+ public final Tuple tupleReply;
+ /**
+ * Connection status. A bitmask of ip_conntrack_status enum flags.
+ *
+ * The status is determined by the parsed attribute value CTA_STATUS, or 0 if the status could
+ * not be parsed successfully (for example, if it was truncated or absent). For the message
+ * from kernel, the valid status is non-zero. For the message from user space, the status may
+ * be 0 (absent).
+ */
+ public final int status;
+ /**
+ * Conntrack timeout.
+ *
+ * The timeout is determined by the parsed attribute value CTA_TIMEOUT, or 0 if the timeout
+ * could not be parsed successfully (for example, if it was truncated or absent). For
+ * IPCTNL_MSG_CT_NEW event, the valid timeout is non-zero. For IPCTNL_MSG_CT_DELETE event, the
+ * timeout is 0 (absent).
+ */
+ public final int timeoutSec;
+
+ private ConntrackMessage() {
+ super(new StructNlMsgHdr());
+ nfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
+
+ // This constructor is only used by #newIPv4TimeoutUpdateRequest which doesn't use these
+ // data member for packing message. Simply fill them to null or 0.
+ tupleOrig = null;
+ tupleReply = null;
+ status = 0;
+ timeoutSec = 0;
+ }
+
+ private ConntrackMessage(@NonNull StructNlMsgHdr header, @NonNull StructNfGenMsg nfGenMsg,
+ @Nullable Tuple tupleOrig, @Nullable Tuple tupleReply, int status, int timeoutSec) {
+ super(header);
+ this.nfGenMsg = nfGenMsg;
+ this.tupleOrig = tupleOrig;
+ this.tupleReply = tupleReply;
+ this.status = status;
+ this.timeoutSec = timeoutSec;
+ }
+
+ /**
+ * Write a netfilter message to {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ mHeader.pack(byteBuffer);
+ nfGenMsg.pack(byteBuffer);
+ }
+
+ public short getMessageType() {
+ return (short) (getHeader().nlmsg_type & ~(NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8));
+ }
+
+ /**
+ * Convert an ip conntrack status to a string.
+ */
+ public static String stringForIpConntrackStatus(int flags) {
+ final StringBuilder sb = new StringBuilder();
+
+ if ((flags & IPS_EXPECTED) != 0) {
+ sb.append("IPS_EXPECTED");
+ }
+ if ((flags & IPS_SEEN_REPLY) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_SEEN_REPLY");
+ }
+ if ((flags & IPS_ASSURED) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_ASSURED");
+ }
+ if ((flags & IPS_CONFIRMED) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_CONFIRMED");
+ }
+ if ((flags & IPS_SRC_NAT) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_SRC_NAT");
+ }
+ if ((flags & IPS_DST_NAT) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_DST_NAT");
+ }
+ if ((flags & IPS_SEQ_ADJUST) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_SEQ_ADJUST");
+ }
+ if ((flags & IPS_SRC_NAT_DONE) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_SRC_NAT_DONE");
+ }
+ if ((flags & IPS_DST_NAT_DONE) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_DST_NAT_DONE");
+ }
+ if ((flags & IPS_DYING) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_DYING");
+ }
+ if ((flags & IPS_FIXED_TIMEOUT) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_FIXED_TIMEOUT");
+ }
+ if ((flags & IPS_TEMPLATE) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_TEMPLATE");
+ }
+ if ((flags & IPS_UNTRACKED) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_UNTRACKED");
+ }
+ if ((flags & IPS_HELPER) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_HELPER");
+ }
+ if ((flags & IPS_OFFLOAD) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_OFFLOAD");
+ }
+ if ((flags & IPS_HW_OFFLOAD) != 0) {
+ if (sb.length() > 0) sb.append("|");
+ sb.append("IPS_HW_OFFLOAD");
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public String toString() {
+ return "ConntrackMessage{"
+ + "nlmsghdr{"
+ + (mHeader == null ? "" : mHeader.toString(OsConstants.NETLINK_NETFILTER))
+ + "}, "
+ + "nfgenmsg{" + nfGenMsg + "}, "
+ + "tuple_orig{" + tupleOrig + "}, "
+ + "tuple_reply{" + tupleReply + "}, "
+ + "status{" + status + "(" + stringForIpConntrackStatus(status) + ")" + "}, "
+ + "timeout_sec{" + Integer.toUnsignedLong(timeoutSec) + "}"
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/InetDiagMessage.java b/common/device/com/android/net/module/util/netlink/InetDiagMessage.java
new file mode 100644
index 00000000..7b200e75
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/InetDiagMessage.java
@@ -0,0 +1,231 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.netlink;
+
+import static android.os.Process.INVALID_UID;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.NETLINK_INET_DIAG;
+
+import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY;
+import static com.android.net.module.util.netlink.NetlinkSocket.DEFAULT_RECV_BUFSIZE;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+
+import android.net.util.SocketUtils;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetSocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A NetlinkMessage subclass for netlink inet_diag messages.
+ *
+ * see also: &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * @hide
+ */
+public class InetDiagMessage extends NetlinkMessage {
+ public static final String TAG = "InetDiagMessage";
+ private static final int TIMEOUT_MS = 500;
+
+ /**
+ * Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
+ * if local and remote are not both null or both non-null.
+ */
+ public static byte[] inetDiagReqV2(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, int family, short flags) {
+ return inetDiagReqV2(protocol, local, remote, family, flags, 0 /* pad */,
+ 0 /* idiagExt */, StructInetDiagReqV2.INET_DIAG_REQ_V2_ALL_STATES);
+ }
+
+ /**
+ * Construct an inet_diag_req_v2 message. This method will throw {@code NullPointerException}
+ * if local and remote are not both null or both non-null.
+ *
+ * @param protocol the request protocol type. This should be set to one of IPPROTO_TCP,
+ * IPPROTO_UDP, or IPPROTO_UDPLITE.
+ * @param local local socket address of the target socket. This will be packed into a
+ * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+ * local or remote address is null.
+ * @param remote remote socket address of the target socket. This will be packed into a
+ * {@Code StructInetDiagSockId}. Request to diagnose for all sockets if both of
+ * local or remote address is null.
+ * @param family the ip family of the request message. This should be set to either AF_INET or
+ * AF_INET6 for IPv4 or IPv6 sockets respectively.
+ * @param flags message flags. See &lt;linux_src&gt;/include/uapi/linux/netlink.h.
+ * @param pad for raw socket protocol specification.
+ * @param idiagExt a set of flags defining what kind of extended information to report.
+ * @param state a bit mask that defines a filter of socket states.
+ *
+ * @return bytes array representation of the message
+ */
+ public static byte[] inetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+ @Nullable InetSocketAddress remote, int family, short flags, int pad, int idiagExt,
+ int state) throws NullPointerException {
+ final byte[] bytes = new byte[StructNlMsgHdr.STRUCT_SIZE + StructInetDiagReqV2.STRUCT_SIZE];
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ final StructNlMsgHdr nlMsgHdr = new StructNlMsgHdr();
+ nlMsgHdr.nlmsg_len = bytes.length;
+ nlMsgHdr.nlmsg_type = SOCK_DIAG_BY_FAMILY;
+ nlMsgHdr.nlmsg_flags = flags;
+ nlMsgHdr.pack(byteBuffer);
+ final StructInetDiagReqV2 inetDiagReqV2 =
+ new StructInetDiagReqV2(protocol, local, remote, family, pad, idiagExt, state);
+
+ inetDiagReqV2.pack(byteBuffer);
+ return bytes;
+ }
+
+ public StructInetDiagMsg mStructInetDiagMsg;
+
+ private InetDiagMessage(StructNlMsgHdr header) {
+ super(header);
+ mStructInetDiagMsg = new StructInetDiagMsg();
+ }
+
+ /**
+ * Parse an inet_diag_req_v2 message from buffer.
+ */
+ public static InetDiagMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+ final InetDiagMessage msg = new InetDiagMessage(header);
+ msg.mStructInetDiagMsg = StructInetDiagMsg.parse(byteBuffer);
+ return msg;
+ }
+
+ private static int lookupUidByFamily(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, int family, short flags,
+ FileDescriptor fd)
+ throws ErrnoException, InterruptedIOException {
+ byte[] msg = inetDiagReqV2(protocol, local, remote, family, flags);
+ NetlinkSocket.sendMessage(fd, msg, 0, msg.length, TIMEOUT_MS);
+ ByteBuffer response = NetlinkSocket.recvMessage(fd, DEFAULT_RECV_BUFSIZE, TIMEOUT_MS);
+
+ final NetlinkMessage nlMsg = NetlinkMessage.parse(response, NETLINK_INET_DIAG);
+ final StructNlMsgHdr hdr = nlMsg.getHeader();
+ if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
+ return INVALID_UID;
+ }
+ if (nlMsg instanceof InetDiagMessage) {
+ return ((InetDiagMessage) nlMsg).mStructInetDiagMsg.idiag_uid;
+ }
+ return INVALID_UID;
+ }
+
+ private static final int[] FAMILY = {AF_INET6, AF_INET};
+
+ private static int lookupUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote, FileDescriptor fd)
+ throws ErrnoException, InterruptedIOException {
+ int uid;
+
+ for (int family : FAMILY) {
+ /**
+ * For exact match lookup, swap local and remote for UDP lookups due to kernel
+ * bug which will not be fixed. See aosp/755889 and
+ * https://www.mail-archive.com/netdev@vger.kernel.org/msg248638.html
+ */
+ if (protocol == IPPROTO_UDP) {
+ uid = lookupUidByFamily(protocol, remote, local, family, NLM_F_REQUEST, fd);
+ } else {
+ uid = lookupUidByFamily(protocol, local, remote, family, NLM_F_REQUEST, fd);
+ }
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ }
+
+ /**
+ * For UDP it's possible for a socket to send packets to arbitrary destinations, even if the
+ * socket is not connected (and even if the socket is connected to a different destination).
+ * If we want this API to work for such packets, then on miss we need to do a second lookup
+ * with only the local address and port filled in.
+ * Always use flags == NLM_F_REQUEST | NLM_F_DUMP for wildcard.
+ */
+ if (protocol == IPPROTO_UDP) {
+ try {
+ InetSocketAddress wildcard = new InetSocketAddress(
+ Inet6Address.getByName("::"), 0);
+ uid = lookupUidByFamily(protocol, local, wildcard, AF_INET6,
+ (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ wildcard = new InetSocketAddress(Inet4Address.getByName("0.0.0.0"), 0);
+ uid = lookupUidByFamily(protocol, local, wildcard, AF_INET,
+ (short) (NLM_F_REQUEST | NLM_F_DUMP), fd);
+ if (uid != INVALID_UID) {
+ return uid;
+ }
+ } catch (UnknownHostException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ return INVALID_UID;
+ }
+
+ /**
+ * Use an inet_diag socket to look up the UID associated with the input local and remote
+ * address/port and protocol of a connection.
+ */
+ public static int getConnectionOwnerUid(int protocol, InetSocketAddress local,
+ InetSocketAddress remote) {
+ int uid = INVALID_UID;
+ FileDescriptor fd = null;
+ try {
+ fd = NetlinkSocket.forProto(NETLINK_INET_DIAG);
+ NetlinkSocket.connectToKernel(fd);
+ uid = lookupUid(protocol, local, remote, fd);
+ } catch (ErrnoException | SocketException | IllegalArgumentException
+ | InterruptedIOException e) {
+ Log.e(TAG, e.toString());
+ } finally {
+ if (fd != null) {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ }
+ }
+ }
+ return uid;
+ }
+
+ @Override
+ public String toString() {
+ return "InetDiagMessage{ "
+ + "nlmsghdr{"
+ + (mHeader == null ? "" : mHeader.toString(NETLINK_INET_DIAG)) + "}, "
+ + "inet_diag_msg{"
+ + (mStructInetDiagMsg == null ? "" : mStructInetDiagMsg.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/NdOption.java b/common/device/com/android/net/module/util/netlink/NdOption.java
new file mode 100644
index 00000000..50a34966
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NdOption.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 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.netlink;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Base class for IPv6 neighbour discovery options.
+ */
+public class NdOption {
+ public static final int STRUCT_SIZE = 2;
+
+ /** The option type. */
+ public final byte type;
+ /** The length of the option in 8-byte units. Actually an unsigned 8-bit integer */
+ public final int length;
+
+ /** Constructs a new NdOption. */
+ NdOption(byte type, int length) {
+ this.type = type;
+ this.length = length;
+ }
+
+ /**
+ * Parses a neighbour discovery option.
+ *
+ * Parses (and consumes) the option if it is of a known type. If the option is of an unknown
+ * type, advances the buffer (so the caller can continue parsing if desired) and returns
+ * {@link #UNKNOWN}. If the option claims a length of 0, returns null because parsing cannot
+ * continue.
+ *
+ * No checks are performed on the length other than ensuring it is not 0, so if a caller wants
+ * to deal with options that might overflow the structure that contains them, it must explicitly
+ * set the buffer's limit to the position at which that structure ends.
+ *
+ * @param buf the buffer to parse.
+ * @return a subclass of {@link NdOption}, or {@code null} for an unknown or malformed option.
+ */
+ public static NdOption parse(ByteBuffer buf) {
+ if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
+
+ // Peek the type without advancing the buffer.
+ byte type = buf.get(buf.position());
+ int length = Byte.toUnsignedInt(buf.get(buf.position() + 1));
+ if (length == 0) return null;
+
+ switch (type) {
+ case StructNdOptPref64.TYPE:
+ return StructNdOptPref64.parse(buf);
+
+ default:
+ int newPosition = Math.min(buf.limit(), buf.position() + length * 8);
+ buf.position(newPosition);
+ return UNKNOWN;
+ }
+ }
+
+ void writeToByteBuffer(ByteBuffer buf) {
+ buf.put(type);
+ buf.put((byte) length);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("NdOption(%d, %d)", Byte.toUnsignedInt(type), length);
+ }
+
+ public static final NdOption UNKNOWN = new NdOption((byte) 0, 0);
+}
diff --git a/common/device/com/android/net/module/util/netlink/NduseroptMessage.java b/common/device/com/android/net/module/util/netlink/NduseroptMessage.java
new file mode 100644
index 00000000..4e3b9f2d
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NduseroptMessage.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2020 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.netlink;
+
+import static android.system.OsConstants.AF_INET6;
+
+import androidx.annotation.NonNull;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A NetlinkMessage subclass for RTM_NEWNDUSEROPT messages.
+ */
+public class NduseroptMessage extends NetlinkMessage {
+ public static final int STRUCT_SIZE = 16;
+
+ static final int NDUSEROPT_SRCADDR = 1;
+
+ /** The address family. Presumably always AF_INET6. */
+ public final byte family;
+ /**
+ * The total length in bytes of the options that follow this structure.
+ * Actually a 16-bit unsigned integer.
+ */
+ public final int opts_len;
+ /** The interface index on which the options were received. */
+ public final int ifindex;
+ /** The ICMP type of the packet that contained the options. */
+ public final byte icmp_type;
+ /** The ICMP code of the packet that contained the options. */
+ public final byte icmp_code;
+
+ /**
+ * ND option that was in this message.
+ * Even though the length field is called "opts_len", the kernel only ever sends one option per
+ * message. It is unlikely that this will ever change as it would break existing userspace code.
+ * But if it does, we can simply update this code, since userspace is typically newer than the
+ * kernel.
+ */
+ public final NdOption option;
+
+ /** The IP address that sent the packet containing the option. */
+ public final InetAddress srcaddr;
+
+ NduseroptMessage(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf)
+ throws UnknownHostException {
+ super(header);
+
+ // The structure itself.
+ buf.order(ByteOrder.nativeOrder()); // Restored in the finally clause inside parse().
+ final int start = buf.position();
+ family = buf.get();
+ buf.get(); // Skip 1 byte of padding.
+ opts_len = Short.toUnsignedInt(buf.getShort());
+ ifindex = buf.getInt();
+ icmp_type = buf.get();
+ icmp_code = buf.get();
+ buf.position(buf.position() + 6); // Skip 6 bytes of padding.
+
+ // The ND option.
+ // Ensure we don't read past opts_len even if the option length is invalid.
+ // Note that this check is not really necessary since if the option length is not valid,
+ // this struct won't be very useful to the caller.
+ buf.order(ByteOrder.BIG_ENDIAN);
+ int oldLimit = buf.limit();
+ buf.limit(start + STRUCT_SIZE + opts_len);
+ try {
+ option = NdOption.parse(buf);
+ } finally {
+ buf.limit(oldLimit);
+ }
+
+ // The source address.
+ int newPosition = start + STRUCT_SIZE + opts_len;
+ if (newPosition >= buf.limit()) {
+ throw new IllegalArgumentException("ND options extend past end of buffer");
+ }
+ buf.position(newPosition);
+
+ StructNlAttr nla = StructNlAttr.parse(buf);
+ if (nla == null || nla.nla_type != NDUSEROPT_SRCADDR || nla.nla_value == null) {
+ throw new IllegalArgumentException("Invalid source address in ND useropt");
+ }
+ if (family == AF_INET6) {
+ // InetAddress.getByAddress only looks at the ifindex if the address type needs one.
+ srcaddr = Inet6Address.getByAddress(null /* hostname */, nla.nla_value, ifindex);
+ } else {
+ srcaddr = InetAddress.getByAddress(nla.nla_value);
+ }
+ }
+
+ /**
+ * Parses a StructNduseroptmsg from a {@link ByteBuffer}.
+ *
+ * @param header the netlink message header.
+ * @param buf The buffer from which to parse the option. The buffer's byte order must be
+ * {@link java.nio.ByteOrder#BIG_ENDIAN}.
+ * @return the parsed option, or {@code null} if the option could not be parsed successfully
+ * (for example, if it was truncated, or if the prefix length code was wrong).
+ */
+ public static NduseroptMessage parse(@NonNull StructNlMsgHdr header, @NonNull ByteBuffer buf) {
+ if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
+ ByteOrder oldOrder = buf.order();
+ try {
+ return new NduseroptMessage(header, buf);
+ } catch (IllegalArgumentException | UnknownHostException | BufferUnderflowException e) {
+ // Not great, but better than throwing an exception that might crash the caller.
+ // Convention in this package is that null indicates that the option was truncated, so
+ // callers must already handle it.
+ return null;
+ } finally {
+ buf.order(oldOrder);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Nduseroptmsg(%d, %d, %d, %d, %d, %s)",
+ family, opts_len, ifindex, Byte.toUnsignedInt(icmp_type),
+ Byte.toUnsignedInt(icmp_code), srcaddr.getHostAddress());
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkConstants.java b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java
new file mode 100644
index 00000000..cf9d2c54
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NetlinkConstants.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2015 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.netlink;
+
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Various constants and static helper methods for netlink communications.
+ *
+ * Values taken from:
+ *
+ * include/uapi/linux/netfilter/nfnetlink.h
+ * include/uapi/linux/netfilter/nfnetlink_conntrack.h
+ * include/uapi/linux/netlink.h
+ * include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class NetlinkConstants {
+ private NetlinkConstants() {}
+
+ public static final int NLA_ALIGNTO = 4;
+ /**
+ * Flag for dumping struct tcp_info.
+ * Corresponding to enum definition in external/strace/linux/inet_diag.h.
+ */
+ public static final int INET_DIAG_MEMINFO = 1;
+
+ public static final int SOCKDIAG_MSG_HEADER_SIZE =
+ StructNlMsgHdr.STRUCT_SIZE + StructInetDiagMsg.STRUCT_SIZE;
+
+ /**
+ * Get the aligned length based on a Short type number.
+ */
+ public static final int alignedLengthOf(short length) {
+ final int intLength = (int) length & 0xffff;
+ return alignedLengthOf(intLength);
+ }
+
+ /**
+ * Get the aligned length based on a Integer type number.
+ */
+ public static final int alignedLengthOf(int length) {
+ if (length <= 0) return 0;
+ return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO);
+ }
+
+ /**
+ * Convert a address family type to a string.
+ */
+ public static String stringForAddressFamily(int family) {
+ if (family == OsConstants.AF_INET) return "AF_INET";
+ if (family == OsConstants.AF_INET6) return "AF_INET6";
+ if (family == OsConstants.AF_NETLINK) return "AF_NETLINK";
+ return String.valueOf(family);
+ }
+
+ /**
+ * Convert a protocol type to a string.
+ */
+ public static String stringForProtocol(int protocol) {
+ if (protocol == OsConstants.IPPROTO_TCP) return "IPPROTO_TCP";
+ if (protocol == OsConstants.IPPROTO_UDP) return "IPPROTO_UDP";
+ return String.valueOf(protocol);
+ }
+
+ /**
+ * Convert a byte array to a hexadecimal string.
+ */
+ public static String hexify(byte[] bytes) {
+ if (bytes == null) return "(null)";
+ return toHexString(bytes, 0, bytes.length);
+ }
+
+ /**
+ * Convert a {@link ByteBuffer} to a hexadecimal string.
+ */
+ public static String hexify(ByteBuffer buffer) {
+ if (buffer == null) return "(null)";
+ return toHexString(
+ buffer.array(), buffer.position(), buffer.remaining());
+ }
+
+ // Known values for struct nlmsghdr nlm_type.
+ public static final short NLMSG_NOOP = 1; // Nothing
+ public static final short NLMSG_ERROR = 2; // Error
+ public static final short NLMSG_DONE = 3; // End of a dump
+ public static final short NLMSG_OVERRUN = 4; // Data lost
+ public static final short NLMSG_MAX_RESERVED = 15; // Max reserved value
+
+ public static final short RTM_NEWLINK = 16;
+ public static final short RTM_DELLINK = 17;
+ public static final short RTM_GETLINK = 18;
+ public static final short RTM_SETLINK = 19;
+ public static final short RTM_NEWADDR = 20;
+ public static final short RTM_DELADDR = 21;
+ public static final short RTM_GETADDR = 22;
+ public static final short RTM_NEWROUTE = 24;
+ public static final short RTM_DELROUTE = 25;
+ public static final short RTM_GETROUTE = 26;
+ public static final short RTM_NEWNEIGH = 28;
+ public static final short RTM_DELNEIGH = 29;
+ public static final short RTM_GETNEIGH = 30;
+ public static final short RTM_NEWRULE = 32;
+ public static final short RTM_DELRULE = 33;
+ public static final short RTM_GETRULE = 34;
+ public static final short RTM_NEWNDUSEROPT = 68;
+
+ // Netfilter netlink message types are presented by two bytes: high byte subsystem and
+ // low byte operation. See the macro NFNL_SUBSYS_ID and NFNL_MSG_TYPE in
+ // include/uapi/linux/netfilter/nfnetlink.h
+ public static final short NFNL_SUBSYS_CTNETLINK = 1;
+
+ public static final short IPCTNL_MSG_CT_NEW = 0;
+ public static final short IPCTNL_MSG_CT_GET = 1;
+ public static final short IPCTNL_MSG_CT_DELETE = 2;
+ public static final short IPCTNL_MSG_CT_GET_CTRZERO = 3;
+ public static final short IPCTNL_MSG_CT_GET_STATS_CPU = 4;
+ public static final short IPCTNL_MSG_CT_GET_STATS = 5;
+ public static final short IPCTNL_MSG_CT_GET_DYING = 6;
+ public static final short IPCTNL_MSG_CT_GET_UNCONFIRMED = 7;
+
+ /* see include/uapi/linux/sock_diag.h */
+ public static final short SOCK_DIAG_BY_FAMILY = 20;
+
+ // Netlink groups.
+ public static final int RTNLGRP_ND_USEROPT = 20;
+ public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1);
+
+ /**
+ * Convert a netlink message type to a string for control message.
+ */
+ @NonNull
+ private static String stringForCtlMsgType(short nlmType) {
+ switch (nlmType) {
+ case NLMSG_NOOP: return "NLMSG_NOOP";
+ case NLMSG_ERROR: return "NLMSG_ERROR";
+ case NLMSG_DONE: return "NLMSG_DONE";
+ case NLMSG_OVERRUN: return "NLMSG_OVERRUN";
+ default: return "unknown control message type: " + String.valueOf(nlmType);
+ }
+ }
+
+ /**
+ * Convert a netlink message type to a string for NETLINK_ROUTE.
+ */
+ @NonNull
+ private static String stringForRtMsgType(short nlmType) {
+ switch (nlmType) {
+ case RTM_NEWLINK: return "RTM_NEWLINK";
+ case RTM_DELLINK: return "RTM_DELLINK";
+ case RTM_GETLINK: return "RTM_GETLINK";
+ case RTM_SETLINK: return "RTM_SETLINK";
+ case RTM_NEWADDR: return "RTM_NEWADDR";
+ case RTM_DELADDR: return "RTM_DELADDR";
+ case RTM_GETADDR: return "RTM_GETADDR";
+ case RTM_NEWROUTE: return "RTM_NEWROUTE";
+ case RTM_DELROUTE: return "RTM_DELROUTE";
+ case RTM_GETROUTE: return "RTM_GETROUTE";
+ case RTM_NEWNEIGH: return "RTM_NEWNEIGH";
+ case RTM_DELNEIGH: return "RTM_DELNEIGH";
+ case RTM_GETNEIGH: return "RTM_GETNEIGH";
+ case RTM_NEWRULE: return "RTM_NEWRULE";
+ case RTM_DELRULE: return "RTM_DELRULE";
+ case RTM_GETRULE: return "RTM_GETRULE";
+ case RTM_NEWNDUSEROPT: return "RTM_NEWNDUSEROPT";
+ default: return "unknown RTM type: " + String.valueOf(nlmType);
+ }
+ }
+
+ /**
+ * Convert a netlink message type to a string for NETLINK_INET_DIAG.
+ */
+ @NonNull
+ private static String stringForInetDiagMsgType(short nlmType) {
+ switch (nlmType) {
+ case SOCK_DIAG_BY_FAMILY: return "SOCK_DIAG_BY_FAMILY";
+ default: return "unknown SOCK_DIAG type: " + String.valueOf(nlmType);
+ }
+ }
+
+ /**
+ * Convert a netlink message type to a string for NETLINK_NETFILTER.
+ */
+ @NonNull
+ private static String stringForNfMsgType(short nlmType) {
+ final byte subsysId = (byte) (nlmType >> 8);
+ final byte msgType = (byte) nlmType;
+ switch (subsysId) {
+ case NFNL_SUBSYS_CTNETLINK:
+ switch (msgType) {
+ case IPCTNL_MSG_CT_NEW: return "IPCTNL_MSG_CT_NEW";
+ case IPCTNL_MSG_CT_GET: return "IPCTNL_MSG_CT_GET";
+ case IPCTNL_MSG_CT_DELETE: return "IPCTNL_MSG_CT_DELETE";
+ case IPCTNL_MSG_CT_GET_CTRZERO: return "IPCTNL_MSG_CT_GET_CTRZERO";
+ case IPCTNL_MSG_CT_GET_STATS_CPU: return "IPCTNL_MSG_CT_GET_STATS_CPU";
+ case IPCTNL_MSG_CT_GET_STATS: return "IPCTNL_MSG_CT_GET_STATS";
+ case IPCTNL_MSG_CT_GET_DYING: return "IPCTNL_MSG_CT_GET_DYING";
+ case IPCTNL_MSG_CT_GET_UNCONFIRMED: return "IPCTNL_MSG_CT_GET_UNCONFIRMED";
+ }
+ break;
+ }
+ return "unknown NETFILTER type: " + String.valueOf(nlmType);
+ }
+
+ /**
+ * Convert a netlink message type to a string by netlink family.
+ */
+ @NonNull
+ public static String stringForNlMsgType(short nlmType, int nlFamily) {
+ // Reserved control messages. The netlink family is ignored.
+ // See NLMSG_MIN_TYPE in include/uapi/linux/netlink.h.
+ if (nlmType <= NLMSG_MAX_RESERVED) return stringForCtlMsgType(nlmType);
+
+ // Netlink family messages. The netlink family is required. Note that the reason for using
+ // if-statement is that switch-case can't be used because the OsConstants.NETLINK_* are
+ // not constant.
+ if (nlFamily == OsConstants.NETLINK_ROUTE) return stringForRtMsgType(nlmType);
+ if (nlFamily == OsConstants.NETLINK_INET_DIAG) return stringForInetDiagMsgType(nlmType);
+ if (nlFamily == OsConstants.NETLINK_NETFILTER) return stringForNfMsgType(nlmType);
+
+ return "unknown type: " + String.valueOf(nlmType);
+ }
+
+ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F' };
+ /**
+ * Convert a byte array to a hexadecimal string.
+ */
+ public static String toHexString(byte[] array, int offset, int length) {
+ char[] buf = new char[length * 2];
+
+ int bufIndex = 0;
+ for (int i = offset; i < offset + length; i++) {
+ byte b = array[i];
+ buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F];
+ buf[bufIndex++] = HEX_DIGITS[b & 0x0F];
+ }
+
+ return new String(buf);
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java b/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java
new file mode 100644
index 00000000..d9fb09e6
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NetlinkErrorMessage.java
@@ -0,0 +1,64 @@
+/*
+ * 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.netlink;
+
+import java.nio.ByteBuffer;
+
+/**
+ * A NetlinkMessage subclass for netlink error messages.
+ *
+ * @hide
+ */
+public class NetlinkErrorMessage extends NetlinkMessage {
+
+ /**
+ * Parse a netlink error message from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the netlink error message.
+ * @return the parsed netlink error message, or {@code null} if the netlink error message
+ * could not be parsed successfully (for example, if it was truncated).
+ */
+ public static NetlinkErrorMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+ final NetlinkErrorMessage errorMsg = new NetlinkErrorMessage(header);
+
+ errorMsg.mNlMsgErr = StructNlMsgErr.parse(byteBuffer);
+ if (errorMsg.mNlMsgErr == null) {
+ return null;
+ }
+
+ return errorMsg;
+ }
+
+ private StructNlMsgErr mNlMsgErr;
+
+ NetlinkErrorMessage(StructNlMsgHdr header) {
+ super(header);
+ mNlMsgErr = null;
+ }
+
+ public StructNlMsgErr getNlMsgError() {
+ return mNlMsgErr;
+ }
+
+ @Override
+ public String toString() {
+ return "NetlinkErrorMessage{ "
+ + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
+ + "nlmsgerr{" + (mNlMsgErr == null ? "" : mNlMsgErr.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkMessage.java b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java
new file mode 100644
index 00000000..f425384f
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NetlinkMessage.java
@@ -0,0 +1,153 @@
+/*
+ * 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.netlink;
+
+import android.system.OsConstants;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+
+/**
+ * NetlinkMessage base class for other, more specific netlink message types.
+ *
+ * Classes that extend NetlinkMessage should:
+ * - implement a public static parse(StructNlMsgHdr, ByteBuffer) method
+ * - returning either null (parse errors) or a new object of the subclass
+ * type (cast-able to NetlinkMessage)
+ *
+ * NetlinkMessage.parse() should be updated to know which nlmsg_type values
+ * correspond with which message subclasses.
+ *
+ * @hide
+ */
+public class NetlinkMessage {
+ private static final String TAG = "NetlinkMessage";
+
+ /**
+ * Parsing netlink messages for reserved control message or specific netlink message. The
+ * netlink family is required for parsing specific netlink message. See man-pages/netlink.
+ */
+ @Nullable
+ public static NetlinkMessage parse(@NonNull ByteBuffer byteBuffer, int nlFamily) {
+ final int startPosition = (byteBuffer != null) ? byteBuffer.position() : -1;
+ final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(byteBuffer);
+ if (nlmsghdr == null) {
+ return null;
+ }
+
+ int payloadLength = NetlinkConstants.alignedLengthOf(nlmsghdr.nlmsg_len);
+ payloadLength -= StructNlMsgHdr.STRUCT_SIZE;
+ if (payloadLength < 0 || payloadLength > byteBuffer.remaining()) {
+ // Malformed message or runt buffer. Pretend the buffer was consumed.
+ byteBuffer.position(byteBuffer.limit());
+ return null;
+ }
+
+ // Reserved control messages. The netlink family is ignored.
+ // See NLMSG_MIN_TYPE in include/uapi/linux/netlink.h.
+ if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
+ return parseCtlMessage(nlmsghdr, byteBuffer, payloadLength);
+ }
+
+ // Netlink family messages. The netlink family is required. Note that the reason for using
+ // if-statement is that switch-case can't be used because the OsConstants.NETLINK_* are
+ // not constant.
+ if (nlFamily == OsConstants.NETLINK_ROUTE) {
+ return parseRtMessage(nlmsghdr, byteBuffer);
+ } else if (nlFamily == OsConstants.NETLINK_INET_DIAG) {
+ return parseInetDiagMessage(nlmsghdr, byteBuffer);
+ } else if (nlFamily == OsConstants.NETLINK_NETFILTER) {
+ return parseNfMessage(nlmsghdr, byteBuffer);
+ }
+
+ return null;
+ }
+
+ protected StructNlMsgHdr mHeader;
+
+ public NetlinkMessage(StructNlMsgHdr nlmsghdr) {
+ mHeader = nlmsghdr;
+ }
+
+ public StructNlMsgHdr getHeader() {
+ return mHeader;
+ }
+
+ @Override
+ public String toString() {
+ // The netlink family is not provided to StructNlMsgHdr#toString because NetlinkMessage
+ // doesn't store the information. So the netlink message type can't be transformed into
+ // a string by StructNlMsgHdr#toString and just keep as an integer. The specific message
+ // which inherits NetlinkMessage could override NetlinkMessage#toString and provide the
+ // specific netlink family to StructNlMsgHdr#toString.
+ return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}";
+ }
+
+ @NonNull
+ private static NetlinkMessage parseCtlMessage(@NonNull StructNlMsgHdr nlmsghdr,
+ @NonNull ByteBuffer byteBuffer, int payloadLength) {
+ switch (nlmsghdr.nlmsg_type) {
+ case NetlinkConstants.NLMSG_ERROR:
+ return (NetlinkMessage) NetlinkErrorMessage.parse(nlmsghdr, byteBuffer);
+ default: {
+ // Other netlink control messages. Just parse the header for now,
+ // pretending the whole message was consumed.
+ byteBuffer.position(byteBuffer.position() + payloadLength);
+ return new NetlinkMessage(nlmsghdr);
+ }
+ }
+ }
+
+ @Nullable
+ private static NetlinkMessage parseRtMessage(@NonNull StructNlMsgHdr nlmsghdr,
+ @NonNull ByteBuffer byteBuffer) {
+ switch (nlmsghdr.nlmsg_type) {
+ case NetlinkConstants.RTM_NEWNEIGH:
+ case NetlinkConstants.RTM_DELNEIGH:
+ case NetlinkConstants.RTM_GETNEIGH:
+ return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
+ case NetlinkConstants.RTM_NEWNDUSEROPT:
+ return (NetlinkMessage) NduseroptMessage.parse(nlmsghdr, byteBuffer);
+ default: return null;
+ }
+ }
+
+ @Nullable
+ private static NetlinkMessage parseInetDiagMessage(@NonNull StructNlMsgHdr nlmsghdr,
+ @NonNull ByteBuffer byteBuffer) {
+ switch (nlmsghdr.nlmsg_type) {
+ case NetlinkConstants.SOCK_DIAG_BY_FAMILY:
+ return (NetlinkMessage) InetDiagMessage.parse(nlmsghdr, byteBuffer);
+ default: return null;
+ }
+ }
+
+ @Nullable
+ private static NetlinkMessage parseNfMessage(@NonNull StructNlMsgHdr nlmsghdr,
+ @NonNull ByteBuffer byteBuffer) {
+ switch (nlmsghdr.nlmsg_type) {
+ case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
+ | NetlinkConstants.IPCTNL_MSG_CT_NEW:
+ case NetlinkConstants.NFNL_SUBSYS_CTNETLINK << 8
+ | NetlinkConstants.IPCTNL_MSG_CT_DELETE:
+ return (NetlinkMessage) ConntrackMessage.parse(nlmsghdr, byteBuffer);
+ default: return null;
+ }
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/NetlinkSocket.java b/common/device/com/android/net/module/util/netlink/NetlinkSocket.java
new file mode 100644
index 00000000..ec326dd7
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/NetlinkSocket.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2015 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.netlink;
+
+import static android.net.util.SocketUtils.makeNetlinkSocketAddress;
+import static android.system.OsConstants.AF_NETLINK;
+import static android.system.OsConstants.EIO;
+import static android.system.OsConstants.EPROTO;
+import static android.system.OsConstants.ETIMEDOUT;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_RCVBUF;
+import static android.system.OsConstants.SO_RCVTIMEO;
+import static android.system.OsConstants.SO_SNDTIMEO;
+
+import android.net.util.SocketUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructTimeval;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+/**
+ * NetlinkSocket
+ *
+ * A small static class to assist with AF_NETLINK socket operations.
+ *
+ * @hide
+ */
+public class NetlinkSocket {
+ private static final String TAG = "NetlinkSocket";
+ private static final long IO_TIMEOUT_MS = 300L;
+
+ public static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
+ public static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
+
+ /**
+ * Send one netlink message to kernel via netlink socket.
+ *
+ * @param nlProto netlink protocol type.
+ * @param msg the raw bytes of netlink message to be sent.
+ */
+ public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
+ final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
+ final FileDescriptor fd = forProto(nlProto);
+
+ try {
+ connectToKernel(fd);
+ sendMessage(fd, msg, 0, msg.length, IO_TIMEOUT_MS);
+ final ByteBuffer bytes = recvMessage(fd, DEFAULT_RECV_BUFSIZE, IO_TIMEOUT_MS);
+ // recvMessage() guaranteed to not return null if it did not throw.
+ final NetlinkMessage response = NetlinkMessage.parse(bytes, nlProto);
+ if (response != null && response instanceof NetlinkErrorMessage
+ && (((NetlinkErrorMessage) response).getNlMsgError() != null)) {
+ final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error;
+ if (errno != 0) {
+ // TODO: consider ignoring EINVAL (-22), which appears to be
+ // normal when probing a neighbor for which the kernel does
+ // not already have / no longer has a link layer address.
+ Log.e(TAG, errPrefix + ", errmsg=" + response.toString());
+ // Note: convert kernel errnos (negative) into userspace errnos (positive).
+ throw new ErrnoException(response.toString(), Math.abs(errno));
+ }
+ } else {
+ final String errmsg;
+ if (response == null) {
+ bytes.position(0);
+ errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
+ } else {
+ errmsg = response.toString();
+ }
+ Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
+ throw new ErrnoException(errmsg, EPROTO);
+ }
+ } catch (InterruptedIOException e) {
+ Log.e(TAG, errPrefix, e);
+ throw new ErrnoException(errPrefix, ETIMEDOUT, e);
+ } catch (SocketException e) {
+ Log.e(TAG, errPrefix, e);
+ throw new ErrnoException(errPrefix, EIO, e);
+ } finally {
+ try {
+ SocketUtils.closeSocket(fd);
+ } catch (IOException e) {
+ // Nothing we can do here
+ }
+ }
+ }
+
+ /**
+ * Create netlink socket with the given netlink protocol type.
+ *
+ * @return fd the fileDescriptor of the socket.
+ * Throw ErrnoException if the exception is thrown.
+ */
+ public static FileDescriptor forProto(int nlProto) throws ErrnoException {
+ final FileDescriptor fd = Os.socket(AF_NETLINK, SOCK_DGRAM, nlProto);
+ Os.setsockoptInt(fd, SOL_SOCKET, SO_RCVBUF, SOCKET_RECV_BUFSIZE);
+ return fd;
+ }
+
+ /**
+ * Connect to kernel via netlink socket.
+ *
+ * Throw ErrnoException, SocketException if the exception is thrown.
+ */
+ public static void connectToKernel(FileDescriptor fd) throws ErrnoException, SocketException {
+ Os.connect(fd, makeNetlinkSocketAddress(0, 0));
+ }
+
+ private static void checkTimeout(long timeoutMs) {
+ if (timeoutMs < 0) {
+ throw new IllegalArgumentException("Negative timeouts not permitted");
+ }
+ }
+
+ /**
+ * Wait up to |timeoutMs| (or until underlying socket error) for a
+ * netlink message of at most |bufsize| size.
+ *
+ * Multi-threaded calls with different timeouts will cause unexpected results.
+ */
+ public static ByteBuffer recvMessage(FileDescriptor fd, int bufsize, long timeoutMs)
+ throws ErrnoException, IllegalArgumentException, InterruptedIOException {
+ checkTimeout(timeoutMs);
+
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
+ int length = Os.read(fd, byteBuffer);
+ if (length == bufsize) {
+ Log.w(TAG, "maximum read");
+ }
+ byteBuffer.position(0);
+ byteBuffer.limit(length);
+ byteBuffer.order(ByteOrder.nativeOrder());
+ return byteBuffer;
+ }
+
+ /**
+ * Send a message to a peer to which this socket has previously connected,
+ * waiting at most |timeoutMs| milliseconds for the send to complete.
+ *
+ * Multi-threaded calls with different timeouts will cause unexpected results.
+ */
+ public static int sendMessage(
+ FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
+ throws ErrnoException, IllegalArgumentException, InterruptedIOException {
+ checkTimeout(timeoutMs);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
+ return Os.write(fd, bytes, offset, count);
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java b/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java
new file mode 100644
index 00000000..a75ef8dd
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/RtNetlinkNeighborMessage.java
@@ -0,0 +1,237 @@
+/*
+ * 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.netlink;
+
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+
+import android.system.OsConstants;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * A NetlinkMessage subclass for rtnetlink neighbor messages.
+ *
+ * see also: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
+ *
+ * @hide
+ */
+public class RtNetlinkNeighborMessage extends NetlinkMessage {
+ public static final short NDA_UNSPEC = 0;
+ public static final short NDA_DST = 1;
+ public static final short NDA_LLADDR = 2;
+ public static final short NDA_CACHEINFO = 3;
+ public static final short NDA_PROBES = 4;
+ public static final short NDA_VLAN = 5;
+ public static final short NDA_PORT = 6;
+ public static final short NDA_VNI = 7;
+ public static final short NDA_IFINDEX = 8;
+ public static final short NDA_MASTER = 9;
+
+ /**
+ * Parse routing socket netlink neighbor message from ByteBuffer.
+ *
+ * @param header netlink message header.
+ * @param byteBuffer the ByteBuffer instance that wraps the raw netlink message bytes.
+ */
+ public static RtNetlinkNeighborMessage parse(StructNlMsgHdr header, ByteBuffer byteBuffer) {
+ final RtNetlinkNeighborMessage neighMsg = new RtNetlinkNeighborMessage(header);
+
+ neighMsg.mNdmsg = StructNdMsg.parse(byteBuffer);
+ if (neighMsg.mNdmsg == null) {
+ return null;
+ }
+
+ // Some of these are message-type dependent, and not always present.
+ final int baseOffset = byteBuffer.position();
+ StructNlAttr nlAttr = StructNlAttr.findNextAttrOfType(NDA_DST, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mDestination = nlAttr.getValueAsInetAddress();
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_LLADDR, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mLinkLayerAddr = nlAttr.nla_value;
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_PROBES, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = StructNlAttr.findNextAttrOfType(NDA_CACHEINFO, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mCacheInfo = StructNdaCacheInfo.parse(nlAttr.getValueAsByteBuffer());
+ }
+
+ final int kMinConsumed = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
+ final int kAdditionalSpace = NetlinkConstants.alignedLengthOf(
+ neighMsg.mHeader.nlmsg_len - kMinConsumed);
+ if (byteBuffer.remaining() < kAdditionalSpace) {
+ byteBuffer.position(byteBuffer.limit());
+ } else {
+ byteBuffer.position(baseOffset + kAdditionalSpace);
+ }
+
+ return neighMsg;
+ }
+
+ /**
+ * A convenience method to create an RTM_GETNEIGH request message.
+ */
+ public static byte[] newGetNeighborsRequest(int seqNo) {
+ final int length = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
+ final byte[] bytes = new byte[length];
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+ nlmsghdr.nlmsg_len = length;
+ nlmsghdr.nlmsg_type = NetlinkConstants.RTM_GETNEIGH;
+ nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
+ nlmsghdr.nlmsg_seq = seqNo;
+ nlmsghdr.pack(byteBuffer);
+
+ final StructNdMsg ndmsg = new StructNdMsg();
+ ndmsg.pack(byteBuffer);
+
+ return bytes;
+ }
+
+ /**
+ * A convenience method to create an RTM_NEWNEIGH message, to modify
+ * the kernel's state information for a specific neighbor.
+ */
+ public static byte[] newNewNeighborMessage(
+ int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
+ final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
+ nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
+ nlmsghdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
+ nlmsghdr.nlmsg_seq = seqNo;
+
+ final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
+ msg.mNdmsg = new StructNdMsg();
+ msg.mNdmsg.ndm_family =
+ (byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
+ msg.mNdmsg.ndm_ifindex = ifIndex;
+ msg.mNdmsg.ndm_state = nudState;
+ msg.mDestination = ip;
+ msg.mLinkLayerAddr = llAddr; // might be null
+
+ final byte[] bytes = new byte[msg.getRequiredSpace()];
+ nlmsghdr.nlmsg_len = bytes.length;
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+ msg.pack(byteBuffer);
+ return bytes;
+ }
+
+ private StructNdMsg mNdmsg;
+ private InetAddress mDestination;
+ private byte[] mLinkLayerAddr;
+ private int mNumProbes;
+ private StructNdaCacheInfo mCacheInfo;
+
+ private RtNetlinkNeighborMessage(StructNlMsgHdr header) {
+ super(header);
+ mNdmsg = null;
+ mDestination = null;
+ mLinkLayerAddr = null;
+ mNumProbes = 0;
+ mCacheInfo = null;
+ }
+
+ public StructNdMsg getNdHeader() {
+ return mNdmsg;
+ }
+
+ public InetAddress getDestination() {
+ return mDestination;
+ }
+
+ public byte[] getLinkLayerAddress() {
+ return mLinkLayerAddr;
+ }
+
+ public int getProbes() {
+ return mNumProbes;
+ }
+
+ public StructNdaCacheInfo getCacheInfo() {
+ return mCacheInfo;
+ }
+
+ private int getRequiredSpace() {
+ int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
+ if (mDestination != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
+ }
+ if (mLinkLayerAddr != null) {
+ spaceRequired += NetlinkConstants.alignedLengthOf(
+ StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
+ }
+ // Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
+ // attributes appended. Fix later, if necessary.
+ return spaceRequired;
+ }
+
+ private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
+ final StructNlAttr nlAttr = new StructNlAttr();
+ nlAttr.nla_type = nlType;
+ nlAttr.nla_value = nlValue;
+ nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
+ nlAttr.pack(byteBuffer);
+ }
+
+ /**
+ * Write a neighbor discovery netlink message to {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ getHeader().pack(byteBuffer);
+ mNdmsg.pack(byteBuffer);
+
+ if (mDestination != null) {
+ packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
+ }
+ if (mLinkLayerAddr != null) {
+ packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
+ }
+ }
+
+ @Override
+ public String toString() {
+ final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
+ return "RtNetlinkNeighborMessage{ "
+ + "nlmsghdr{"
+ + (mHeader == null ? "" : mHeader.toString(OsConstants.NETLINK_ROUTE)) + "}, "
+ + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, "
+ + "destination{" + ipLiteral + "} "
+ + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} "
+ + "probes{" + mNumProbes + "} "
+ + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructInetDiagMsg.java b/common/device/com/android/net/module/util/netlink/StructInetDiagMsg.java
new file mode 100644
index 00000000..205656e3
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructInetDiagMsg.java
@@ -0,0 +1,62 @@
+/*
+ * 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.netlink;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct inet_diag_msg
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_msg {
+ * __u8 idiag_family;
+ * __u8 idiag_state;
+ * __u8 idiag_timer;
+ * __u8 idiag_retrans;
+ * struct inet_diag_sockid id;
+ * __u32 idiag_expires;
+ * __u32 idiag_rqueue;
+ * __u32 idiag_wqueue;
+ * __u32 idiag_uid;
+ * __u32 idiag_inode;
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagMsg {
+ public static final int STRUCT_SIZE = 4 + StructInetDiagSockId.STRUCT_SIZE + 20;
+ private static final int IDIAG_UID_OFFSET = StructNlMsgHdr.STRUCT_SIZE + 4
+ + StructInetDiagSockId.STRUCT_SIZE + 12;
+ public int idiag_uid;
+
+ /**
+ * Parse inet diag netlink message from buffer.
+ */
+ public static StructInetDiagMsg parse(ByteBuffer byteBuffer) {
+ StructInetDiagMsg struct = new StructInetDiagMsg();
+ struct.idiag_uid = byteBuffer.getInt(IDIAG_UID_OFFSET);
+ return struct;
+ }
+
+ @Override
+ public String toString() {
+ return "StructInetDiagMsg{ "
+ + "idiag_uid{" + idiag_uid + "}, "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructInetDiagReqV2.java b/common/device/com/android/net/module/util/netlink/StructInetDiagReqV2.java
new file mode 100644
index 00000000..6eef8653
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructInetDiagReqV2.java
@@ -0,0 +1,100 @@
+/*
+ * 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.netlink;
+
+import androidx.annotation.Nullable;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_req_v2 {
+ * __u8 sdiag_family;
+ * __u8 sdiag_protocol;
+ * __u8 idiag_ext;
+ * __u8 pad;
+ * __u32 idiag_states;
+ * struct inet_diag_sockid id;
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagReqV2 {
+ public static final int STRUCT_SIZE = 8 + StructInetDiagSockId.STRUCT_SIZE;
+
+ private final byte mSdiagFamily;
+ private final byte mSdiagProtocol;
+ private final byte mIdiagExt;
+ private final byte mPad;
+ private final StructInetDiagSockId mId;
+ private final int mState;
+ public static final int INET_DIAG_REQ_V2_ALL_STATES = (int) 0xffffffff;
+
+ public StructInetDiagReqV2(int protocol, InetSocketAddress local, InetSocketAddress remote,
+ int family) {
+ this(protocol, local, remote, family, 0 /* pad */, 0 /* extension */,
+ INET_DIAG_REQ_V2_ALL_STATES);
+ }
+
+ public StructInetDiagReqV2(int protocol, @Nullable InetSocketAddress local,
+ @Nullable InetSocketAddress remote, int family, int pad, int extension, int state)
+ throws NullPointerException {
+ mSdiagFamily = (byte) family;
+ mSdiagProtocol = (byte) protocol;
+ // Request for all sockets if no specific socket is requested. Specify the local and remote
+ // socket address information for target request socket.
+ if ((local == null) != (remote == null)) {
+ throw new NullPointerException("Local and remote must be both null or both non-null");
+ }
+ mId = ((local != null && remote != null) ? new StructInetDiagSockId(local, remote) : null);
+ mPad = (byte) pad;
+ mIdiagExt = (byte) extension;
+ mState = state;
+ }
+
+ /**
+ * Write the int diag request v2 message to ByteBuffer.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ // The ByteOrder must have already been set by the caller.
+ byteBuffer.put((byte) mSdiagFamily);
+ byteBuffer.put((byte) mSdiagProtocol);
+ byteBuffer.put((byte) mIdiagExt);
+ byteBuffer.put((byte) mPad);
+ byteBuffer.putInt(mState);
+ if (mId != null) mId.pack(byteBuffer);
+ }
+
+ @Override
+ public String toString() {
+ final String familyStr = NetlinkConstants.stringForAddressFamily(mSdiagFamily);
+ final String protocolStr = NetlinkConstants.stringForAddressFamily(mSdiagProtocol);
+
+ return "StructInetDiagReqV2{ "
+ + "sdiag_family{" + familyStr + "}, "
+ + "sdiag_protocol{" + protocolStr + "}, "
+ + "idiag_ext{" + mIdiagExt + ")}, "
+ + "pad{" + mPad + "}, "
+ + "idiag_states{" + Integer.toHexString(mState) + "}, "
+ + ((mId != null) ? mId.toString() : "inet_diag_sockid=null")
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructInetDiagSockId.java b/common/device/com/android/net/module/util/netlink/StructInetDiagSockId.java
new file mode 100644
index 00000000..95d60e53
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructInetDiagSockId.java
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+
+package com.android.net.module.util.netlink;
+
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct inet_diag_req_v2
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/inet_diag.h
+ *
+ * struct inet_diag_sockid {
+ * __be16 idiag_sport;
+ * __be16 idiag_dport;
+ * __be32 idiag_src[4];
+ * __be32 idiag_dst[4];
+ * __u32 idiag_if;
+ * __u32 idiag_cookie[2];
+ * #define INET_DIAG_NOCOOKIE (~0U)
+ * };
+ *
+ * @hide
+ */
+public class StructInetDiagSockId {
+ public static final int STRUCT_SIZE = 48;
+
+ private static final byte[] INET_DIAG_NOCOOKIE = new byte[]{
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
+ (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff};
+ private static final byte[] IPV4_PADDING = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ private final InetSocketAddress mLocSocketAddress;
+ private final InetSocketAddress mRemSocketAddress;
+
+ public StructInetDiagSockId(InetSocketAddress loc, InetSocketAddress rem) {
+ mLocSocketAddress = loc;
+ mRemSocketAddress = rem;
+ }
+
+ /**
+ * Write inet diag socket id message to ByteBuffer in big endian.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ byteBuffer.order(BIG_ENDIAN);
+ byteBuffer.putShort((short) mLocSocketAddress.getPort());
+ byteBuffer.putShort((short) mRemSocketAddress.getPort());
+ byteBuffer.put(mLocSocketAddress.getAddress().getAddress());
+ if (mLocSocketAddress.getAddress() instanceof Inet4Address) {
+ byteBuffer.put(IPV4_PADDING);
+ }
+ byteBuffer.put(mRemSocketAddress.getAddress().getAddress());
+ if (mRemSocketAddress.getAddress() instanceof Inet4Address) {
+ byteBuffer.put(IPV4_PADDING);
+ }
+ byteBuffer.order(ByteOrder.nativeOrder());
+ byteBuffer.putInt(0);
+ byteBuffer.put(INET_DIAG_NOCOOKIE);
+ }
+
+ @Override
+ public String toString() {
+ return "StructInetDiagSockId{ "
+ + "idiag_sport{" + mLocSocketAddress.getPort() + "}, "
+ + "idiag_dport{" + mRemSocketAddress.getPort() + "}, "
+ + "idiag_src{" + mLocSocketAddress.getAddress().getHostAddress() + "}, "
+ + "idiag_dst{" + mRemSocketAddress.getAddress().getHostAddress() + "}, "
+ + "idiag_if{" + 0 + "} "
+ + "idiag_cookie{INET_DIAG_NOCOOKIE}"
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNdMsg.java b/common/device/com/android/net/module/util/netlink/StructNdMsg.java
new file mode 100644
index 00000000..53ce8991
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNdMsg.java
@@ -0,0 +1,198 @@
+/*
+ * 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.netlink;
+
+import android.system.OsConstants;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct ndmsg
+ *
+ * see: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
+ *
+ * @hide
+ */
+public class StructNdMsg {
+ // Already aligned.
+ public static final int STRUCT_SIZE = 12;
+
+ // Neighbor Cache Entry States
+ public static final short NUD_NONE = 0x00;
+ public static final short NUD_INCOMPLETE = 0x01;
+ public static final short NUD_REACHABLE = 0x02;
+ public static final short NUD_STALE = 0x04;
+ public static final short NUD_DELAY = 0x08;
+ public static final short NUD_PROBE = 0x10;
+ public static final short NUD_FAILED = 0x20;
+ public static final short NUD_NOARP = 0x40;
+ public static final short NUD_PERMANENT = 0x80;
+
+ /**
+ * Convert neighbor cache entry state integer to string.
+ */
+ public static String stringForNudState(short nudState) {
+ switch (nudState) {
+ case NUD_NONE: return "NUD_NONE";
+ case NUD_INCOMPLETE: return "NUD_INCOMPLETE";
+ case NUD_REACHABLE: return "NUD_REACHABLE";
+ case NUD_STALE: return "NUD_STALE";
+ case NUD_DELAY: return "NUD_DELAY";
+ case NUD_PROBE: return "NUD_PROBE";
+ case NUD_FAILED: return "NUD_FAILED";
+ case NUD_NOARP: return "NUD_NOARP";
+ case NUD_PERMANENT: return "NUD_PERMANENT";
+ default:
+ return "unknown NUD state: " + String.valueOf(nudState);
+ }
+ }
+
+ /**
+ * Check whether a neighbor is connected or not.
+ */
+ public static boolean isNudStateConnected(short nudState) {
+ return ((nudState & (NUD_PERMANENT | NUD_NOARP | NUD_REACHABLE)) != 0);
+ }
+
+ /**
+ * Check whether a neighbor is in the valid NUD state or not.
+ */
+ public static boolean isNudStateValid(short nudState) {
+ return (isNudStateConnected(nudState)
+ || ((nudState & (NUD_PROBE | NUD_STALE | NUD_DELAY)) != 0));
+ }
+
+ // Neighbor Cache Entry Flags
+ public static byte NTF_USE = (byte) 0x01;
+ public static byte NTF_SELF = (byte) 0x02;
+ public static byte NTF_MASTER = (byte) 0x04;
+ public static byte NTF_PROXY = (byte) 0x08;
+ public static byte NTF_ROUTER = (byte) 0x80;
+
+ private static String stringForNudFlags(byte flags) {
+ final StringBuilder sb = new StringBuilder();
+ if ((flags & NTF_USE) != 0) {
+ sb.append("NTF_USE");
+ }
+ if ((flags & NTF_SELF) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NTF_SELF");
+ }
+ if ((flags & NTF_MASTER) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NTF_MASTER");
+ }
+ if ((flags & NTF_PROXY) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NTF_PROXY");
+ }
+ if ((flags & NTF_ROUTER) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NTF_ROUTER");
+ }
+ return sb.toString();
+ }
+
+ private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ /**
+ * Parse a neighbor discovery netlink message header from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the nd netlink message header.
+ * @return the parsed nd netlink message header, or {@code null} if the nd netlink message
+ * header could not be parsed successfully (for example, if it was truncated).
+ */
+ public static StructNdMsg parse(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) return null;
+
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the possible
+ // exception of usage within unittests.
+ final StructNdMsg struct = new StructNdMsg();
+ struct.ndm_family = byteBuffer.get();
+ final byte pad1 = byteBuffer.get();
+ final short pad2 = byteBuffer.getShort();
+ struct.ndm_ifindex = byteBuffer.getInt();
+ struct.ndm_state = byteBuffer.getShort();
+ struct.ndm_flags = byteBuffer.get();
+ struct.ndm_type = byteBuffer.get();
+ return struct;
+ }
+
+ public byte ndm_family;
+ public int ndm_ifindex;
+ public short ndm_state;
+ public byte ndm_flags;
+ public byte ndm_type;
+
+ public StructNdMsg() {
+ ndm_family = (byte) OsConstants.AF_UNSPEC;
+ }
+
+ /**
+ * Write the neighbor discovery message header to {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the exception
+ // of usage within unittests.
+ byteBuffer.put(ndm_family);
+ byteBuffer.put((byte) 0); // pad1
+ byteBuffer.putShort((short) 0); // pad2
+ byteBuffer.putInt(ndm_ifindex);
+ byteBuffer.putShort(ndm_state);
+ byteBuffer.put(ndm_flags);
+ byteBuffer.put(ndm_type);
+ }
+
+ /**
+ * Check whether a neighbor is connected or not.
+ */
+ public boolean nudConnected() {
+ return isNudStateConnected(ndm_state);
+ }
+
+ /**
+ * Check whether a neighbor is in the valid NUD state or not.
+ */
+ public boolean nudValid() {
+ return isNudStateValid(ndm_state);
+ }
+
+ @Override
+ public String toString() {
+ final String stateStr = "" + ndm_state + " (" + stringForNudState(ndm_state) + ")";
+ final String flagsStr = "" + ndm_flags + " (" + stringForNudFlags(ndm_flags) + ")";
+ return "StructNdMsg{ "
+ + "family{" + NetlinkConstants.stringForAddressFamily((int) ndm_family) + "}, "
+ + "ifindex{" + ndm_ifindex + "}, "
+ + "state{" + stateStr + "}, "
+ + "flags{" + flagsStr + "}, "
+ + "type{" + ndm_type + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java b/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java
new file mode 100644
index 00000000..bde69831
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNdOptPref64.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 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.netlink;
+
+import android.net.IpPrefix;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+
+/**
+ * The PREF64 router advertisement option. RFC 8781.
+ *
+ * 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
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Scaled Lifetime | PLC |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | Highest 96 bits of the Prefix |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+public class StructNdOptPref64 extends NdOption {
+ public static final int STRUCT_SIZE = 16;
+ public static final int TYPE = 38;
+ public static final byte LENGTH = 2;
+
+ private static final String TAG = StructNdOptPref64.class.getSimpleName();
+
+ /**
+ * How many seconds the prefix is expected to remain valid.
+ * Valid values are from 0 to 65528 in multiples of 8.
+ */
+ public final int lifetime;
+ /** The NAT64 prefix. */
+ @NonNull public final IpPrefix prefix;
+
+ static int plcToPrefixLength(int plc) {
+ switch (plc) {
+ case 0: return 96;
+ case 1: return 64;
+ case 2: return 56;
+ case 3: return 48;
+ case 4: return 40;
+ case 5: return 32;
+ default:
+ throw new IllegalArgumentException("Invalid prefix length code " + plc);
+ }
+ }
+
+ static int prefixLengthToPlc(int prefixLength) {
+ switch (prefixLength) {
+ case 96: return 0;
+ case 64: return 1;
+ case 56: return 2;
+ case 48: return 3;
+ case 40: return 4;
+ case 32: return 5;
+ default:
+ throw new IllegalArgumentException("Invalid prefix length " + prefixLength);
+ }
+ }
+
+ /**
+ * Returns the 2-byte "scaled lifetime and prefix length code" field: 13-bit lifetime, 3-bit PLC
+ */
+ static short getScaledLifetimePlc(int lifetime, int prefixLengthCode) {
+ return (short) ((lifetime & 0xfff8) | (prefixLengthCode & 0x7));
+ }
+
+ public StructNdOptPref64(@NonNull IpPrefix prefix, int lifetime) {
+ super((byte) TYPE, LENGTH);
+
+ Objects.requireNonNull(prefix, "prefix must not be null");
+ if (!(prefix.getAddress() instanceof Inet6Address)) {
+ throw new IllegalArgumentException("Must be an IPv6 prefix: " + prefix);
+ }
+ prefixLengthToPlc(prefix.getPrefixLength()); // Throw if the prefix length is invalid.
+ this.prefix = prefix;
+
+ if (lifetime < 0 || lifetime > 0xfff8) {
+ throw new IllegalArgumentException("Invalid lifetime " + lifetime);
+ }
+ this.lifetime = lifetime & 0xfff8;
+ }
+
+ private StructNdOptPref64(@NonNull ByteBuffer buf) {
+ super(buf.get(), Byte.toUnsignedInt(buf.get()));
+ if (type != TYPE) throw new IllegalArgumentException("Invalid type " + type);
+ if (length != LENGTH) throw new IllegalArgumentException("Invalid length " + length);
+
+ int scaledLifetimePlc = Short.toUnsignedInt(buf.getShort());
+ lifetime = scaledLifetimePlc & 0xfff8;
+
+ byte[] addressBytes = new byte[16];
+ buf.get(addressBytes, 0, 12);
+ InetAddress addr;
+ try {
+ addr = InetAddress.getByAddress(addressBytes);
+ } catch (UnknownHostException e) {
+ throw new AssertionError("16-byte array not valid InetAddress?");
+ }
+ prefix = new IpPrefix(addr, plcToPrefixLength(scaledLifetimePlc & 7));
+ }
+
+ /**
+ * Parses an option from a {@link ByteBuffer}.
+ *
+ * @param buf The buffer from which to parse the option. The buffer's byte order must be
+ * {@link java.nio.ByteOrder#BIG_ENDIAN}.
+ * @return the parsed option, or {@code null} if the option could not be parsed successfully
+ * (for example, if it was truncated, or if the prefix length code was wrong).
+ */
+ public static StructNdOptPref64 parse(@NonNull ByteBuffer buf) {
+ if (buf == null || buf.remaining() < STRUCT_SIZE) return null;
+ try {
+ return new StructNdOptPref64(buf);
+ } catch (IllegalArgumentException e) {
+ // Not great, but better than throwing an exception that might crash the caller.
+ // Convention in this package is that null indicates that the option was truncated, so
+ // callers must already handle it.
+ Log.d(TAG, "Invalid PREF64 option: " + e);
+ return null;
+ }
+ }
+
+ protected void writeToByteBuffer(ByteBuffer buf) {
+ super.writeToByteBuffer(buf);
+ buf.putShort(getScaledLifetimePlc(lifetime, prefixLengthToPlc(prefix.getPrefixLength())));
+ buf.put(prefix.getRawAddress(), 0, 12);
+ }
+
+ /** Outputs the wire format of the option to a new big-endian ByteBuffer. */
+ public ByteBuffer toByteBuffer() {
+ ByteBuffer buf = ByteBuffer.allocate(STRUCT_SIZE);
+ writeToByteBuffer(buf);
+ buf.flip();
+ return buf;
+ }
+
+ @Override
+ @NonNull
+ public String toString() {
+ return String.format("NdOptPref64(%s, %d)", prefix, lifetime);
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNdaCacheInfo.java b/common/device/com/android/net/module/util/netlink/StructNdaCacheInfo.java
new file mode 100644
index 00000000..79d5ff4a
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNdaCacheInfo.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 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.netlink;
+
+import android.system.Os;
+import android.system.OsConstants;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct nda_cacheinfo
+ *
+ * see: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
+ *
+ * @hide
+ */
+public class StructNdaCacheInfo {
+ // Already aligned.
+ public static final int STRUCT_SIZE = 16;
+
+ private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ /**
+ * Parse a nd cacheinfo netlink attribute from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the nd cacheinfo attribute.
+ * @return the parsed nd cacheinfo attribute, or {@code null} if the nd cacheinfo attribute
+ * could not be parsed successfully (for example, if it was truncated).
+ */
+ public static StructNdaCacheInfo parse(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) return null;
+
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the possible
+ // exception of usage within unittests.
+ final StructNdaCacheInfo struct = new StructNdaCacheInfo();
+ struct.ndm_used = byteBuffer.getInt();
+ struct.ndm_confirmed = byteBuffer.getInt();
+ struct.ndm_updated = byteBuffer.getInt();
+ struct.ndm_refcnt = byteBuffer.getInt();
+ return struct;
+ }
+
+ // TODO: investigate whether this can change during device runtime and
+ // decide what (if anything) should be done about that.
+ private static final long CLOCK_TICKS_PER_SECOND = Os.sysconf(OsConstants._SC_CLK_TCK);
+
+ private static long ticksToMilliSeconds(int intClockTicks) {
+ final long longClockTicks = (long) intClockTicks & 0xffffffff;
+ return (longClockTicks * 1000) / CLOCK_TICKS_PER_SECOND;
+ }
+
+ /**
+ * Explanatory notes, for reference.
+ *
+ * Before being returned to user space, the neighbor entry times are
+ * converted to clock_t's like so:
+ *
+ * ndm_used = jiffies_to_clock_t(now - neigh->used);
+ * ndm_confirmed = jiffies_to_clock_t(now - neigh->confirmed);
+ * ndm_updated = jiffies_to_clock_t(now - neigh->updated);
+ *
+ * meaning that these values are expressed as "clock ticks ago". To
+ * convert these clock ticks to seconds divide by sysconf(_SC_CLK_TCK).
+ * When _SC_CLK_TCK is 100, for example, the ndm_* times are expressed
+ * in centiseconds.
+ *
+ * These values are unsigned, but fortunately being expressed as "some
+ * clock ticks ago", these values are typically very small (and
+ * 2^31 centiseconds = 248 days).
+ *
+ * By observation, it appears that:
+ * ndm_used: the last time ARP/ND took place for this neighbor
+ * ndm_confirmed: the last time ARP/ND succeeded for this neighbor OR
+ * higher layer confirmation (TCP or MSG_CONFIRM)
+ * was received
+ * ndm_updated: the time when the current NUD state was entered
+ */
+ public int ndm_used;
+ public int ndm_confirmed;
+ public int ndm_updated;
+ public int ndm_refcnt;
+
+ public StructNdaCacheInfo() {}
+
+ /**
+ * The last time ARP/ND took place for this neighbor.
+ */
+ public long lastUsed() {
+ return ticksToMilliSeconds(ndm_used);
+ }
+
+ /**
+ * The last time ARP/ND succeeded for this neighbor or higher layer confirmation (TCP or
+ * MSG_CONFIRM) was received.
+ */
+ public long lastConfirmed() {
+ return ticksToMilliSeconds(ndm_confirmed);
+ }
+
+ /**
+ * The time when the current NUD state was entered.
+ */
+ public long lastUpdated() {
+ return ticksToMilliSeconds(ndm_updated);
+ }
+
+ @Override
+ public String toString() {
+ return "NdaCacheInfo{ "
+ + "ndm_used{" + lastUsed() + "}, "
+ + "ndm_confirmed{" + lastConfirmed() + "}, "
+ + "ndm_updated{" + lastUpdated() + "}, "
+ + "ndm_refcnt{" + ndm_refcnt + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNfGenMsg.java b/common/device/com/android/net/module/util/netlink/StructNfGenMsg.java
new file mode 100644
index 00000000..2de5490b
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNfGenMsg.java
@@ -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.
+ */
+
+package com.android.net.module.util.netlink;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+/**
+ * struct nfgenmsg
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink.h
+ *
+ * @hide
+ */
+public class StructNfGenMsg {
+ public static final int STRUCT_SIZE = 2 + Short.BYTES;
+
+ public static final int NFNETLINK_V0 = 0;
+
+ public final byte nfgen_family;
+ public final byte version;
+ public final short res_id; // N.B.: this is big endian in the kernel
+
+ /**
+ * Parse a netfilter netlink header from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the netfilter netlink header.
+ * @return the parsed netfilter netlink header, or {@code null} if the netfilter netlink header
+ * could not be parsed successfully (for example, if it was truncated).
+ */
+ @Nullable
+ public static StructNfGenMsg parse(@NonNull ByteBuffer byteBuffer) {
+ Objects.requireNonNull(byteBuffer);
+
+ if (!hasAvailableSpace(byteBuffer)) return null;
+
+ final byte nfgen_family = byteBuffer.get();
+ final byte version = byteBuffer.get();
+
+ final ByteOrder originalOrder = byteBuffer.order();
+ byteBuffer.order(ByteOrder.BIG_ENDIAN);
+ final short res_id = byteBuffer.getShort();
+ byteBuffer.order(originalOrder);
+
+ return new StructNfGenMsg(nfgen_family, version, res_id);
+ }
+
+ public StructNfGenMsg(byte family, byte ver, short id) {
+ nfgen_family = family;
+ version = ver;
+ res_id = id;
+ }
+
+ public StructNfGenMsg(byte family) {
+ nfgen_family = family;
+ version = (byte) NFNETLINK_V0;
+ res_id = (short) 0;
+ }
+
+ /**
+ * Write a netfilter netlink header to a {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ byteBuffer.put(nfgen_family);
+ byteBuffer.put(version);
+
+ final ByteOrder originalOrder = byteBuffer.order();
+ byteBuffer.order(ByteOrder.BIG_ENDIAN);
+ byteBuffer.putShort(res_id);
+ byteBuffer.order(originalOrder);
+ }
+
+ private static boolean hasAvailableSpace(@NonNull ByteBuffer byteBuffer) {
+ return byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ @Override
+ public String toString() {
+ final String familyStr = NetlinkConstants.stringForAddressFamily(nfgen_family);
+
+ return "NfGenMsg{ "
+ + "nfgen_family{" + familyStr + "}, "
+ + "version{" + Byte.toUnsignedInt(version) + "}, "
+ + "res_id{" + Short.toUnsignedInt(res_id) + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNlAttr.java b/common/device/com/android/net/module/util/netlink/StructNlAttr.java
new file mode 100644
index 00000000..80f00579
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNlAttr.java
@@ -0,0 +1,315 @@
+/*
+ * 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.netlink;
+
+import androidx.annotation.Nullable;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * struct nlattr
+ *
+ * see: &lt;linux_src&gt;/include/uapi/linux/netlink.h
+ *
+ * @hide
+ */
+public class StructNlAttr {
+ // Already aligned.
+ public static final int NLA_HEADERLEN = 4;
+ public static final int NLA_F_NESTED = (1 << 15);
+
+ /**
+ * Set carries nested attributes bit.
+ */
+ public static short makeNestedType(short type) {
+ return (short) (type | NLA_F_NESTED);
+ }
+
+ /**
+ * Peek and parse the netlink attribute from {@link ByteBuffer}.
+ *
+ * Return a (length, type) object only, without consuming any bytes in
+ * |byteBuffer| and without copying or interpreting any value bytes.
+ * This is used for scanning over a packed set of struct nlattr's,
+ * looking for instances of a particular type.
+ */
+ public static StructNlAttr peek(ByteBuffer byteBuffer) {
+ if (byteBuffer == null || byteBuffer.remaining() < NLA_HEADERLEN) {
+ return null;
+ }
+ final int baseOffset = byteBuffer.position();
+
+ final StructNlAttr struct = new StructNlAttr();
+ final ByteOrder originalOrder = byteBuffer.order();
+ byteBuffer.order(ByteOrder.nativeOrder());
+ try {
+ struct.nla_len = byteBuffer.getShort();
+ struct.nla_type = byteBuffer.getShort();
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
+
+ byteBuffer.position(baseOffset);
+ if (struct.nla_len < NLA_HEADERLEN) {
+ // Malformed.
+ return null;
+ }
+ return struct;
+ }
+
+ /**
+ * Parse a netlink attribute from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the netlink attriute.
+ * @return the parsed netlink attribute, or {@code null} if the netlink attribute
+ * could not be parsed successfully (for example, if it was truncated).
+ */
+ public static StructNlAttr parse(ByteBuffer byteBuffer) {
+ final StructNlAttr struct = peek(byteBuffer);
+ if (struct == null || byteBuffer.remaining() < struct.getAlignedLength()) {
+ return null;
+ }
+
+ final int baseOffset = byteBuffer.position();
+ byteBuffer.position(baseOffset + NLA_HEADERLEN);
+
+ int valueLen = ((int) struct.nla_len) & 0xffff;
+ valueLen -= NLA_HEADERLEN;
+ if (valueLen > 0) {
+ struct.nla_value = new byte[valueLen];
+ byteBuffer.get(struct.nla_value, 0, valueLen);
+ byteBuffer.position(baseOffset + struct.getAlignedLength());
+ }
+ return struct;
+ }
+
+ /**
+ * Find next netlink attribute with a given type from {@link ByteBuffer}.
+ *
+ * @param attrType The given netlink attribute type is requested for.
+ * @param byteBuffer The buffer from which to find the netlink attribute.
+ * @return the found netlink attribute, or {@code null} if the netlink attribute could not be
+ * found or parsed successfully (for example, if it was truncated).
+ */
+ @Nullable
+ public static StructNlAttr findNextAttrOfType(short attrType,
+ @Nullable ByteBuffer byteBuffer) {
+ while (byteBuffer != null && byteBuffer.remaining() > 0) {
+ final StructNlAttr nlAttr = StructNlAttr.peek(byteBuffer);
+ if (nlAttr == null) {
+ break;
+ }
+ if (nlAttr.nla_type == attrType) {
+ return StructNlAttr.parse(byteBuffer);
+ }
+ if (byteBuffer.remaining() < nlAttr.getAlignedLength()) {
+ break;
+ }
+ byteBuffer.position(byteBuffer.position() + nlAttr.getAlignedLength());
+ }
+ return null;
+ }
+
+ public short nla_len = (short) NLA_HEADERLEN;
+ public short nla_type;
+ public byte[] nla_value;
+
+ public StructNlAttr() {}
+
+ public StructNlAttr(short type, byte value) {
+ nla_type = type;
+ setValue(new byte[1]);
+ nla_value[0] = value;
+ }
+
+ public StructNlAttr(short type, short value) {
+ this(type, value, ByteOrder.nativeOrder());
+ }
+
+ public StructNlAttr(short type, short value, ByteOrder order) {
+ nla_type = type;
+ setValue(new byte[Short.BYTES]);
+ final ByteBuffer buf = getValueAsByteBuffer();
+ final ByteOrder originalOrder = buf.order();
+ try {
+ buf.order(order);
+ buf.putShort(value);
+ } finally {
+ buf.order(originalOrder);
+ }
+ }
+
+ public StructNlAttr(short type, int value) {
+ this(type, value, ByteOrder.nativeOrder());
+ }
+
+ public StructNlAttr(short type, int value, ByteOrder order) {
+ nla_type = type;
+ setValue(new byte[Integer.BYTES]);
+ final ByteBuffer buf = getValueAsByteBuffer();
+ final ByteOrder originalOrder = buf.order();
+ try {
+ buf.order(order);
+ buf.putInt(value);
+ } finally {
+ buf.order(originalOrder);
+ }
+ }
+
+ public StructNlAttr(short type, InetAddress ip) {
+ nla_type = type;
+ setValue(ip.getAddress());
+ }
+
+ public StructNlAttr(short type, StructNlAttr... nested) {
+ this();
+ nla_type = makeNestedType(type);
+
+ int payloadLength = 0;
+ for (StructNlAttr nla : nested) payloadLength += nla.getAlignedLength();
+ setValue(new byte[payloadLength]);
+
+ final ByteBuffer buf = getValueAsByteBuffer();
+ for (StructNlAttr nla : nested) {
+ nla.pack(buf);
+ }
+ }
+
+ /**
+ * Get aligned attribute length.
+ */
+ public int getAlignedLength() {
+ return NetlinkConstants.alignedLengthOf(nla_len);
+ }
+
+ /**
+ * Get attribute value as BE16.
+ */
+ public short getValueAsBe16(short defaultValue) {
+ final ByteBuffer byteBuffer = getValueAsByteBuffer();
+ if (byteBuffer == null || byteBuffer.remaining() != Short.BYTES) {
+ return defaultValue;
+ }
+ final ByteOrder originalOrder = byteBuffer.order();
+ try {
+ byteBuffer.order(ByteOrder.BIG_ENDIAN);
+ return byteBuffer.getShort();
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
+ }
+
+ /**
+ * Get attribute value as BE32.
+ */
+ public int getValueAsBe32(int defaultValue) {
+ final ByteBuffer byteBuffer = getValueAsByteBuffer();
+ if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
+ return defaultValue;
+ }
+ final ByteOrder originalOrder = byteBuffer.order();
+ try {
+ byteBuffer.order(ByteOrder.BIG_ENDIAN);
+ return byteBuffer.getInt();
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
+ }
+
+ /**
+ * Get attribute value as ByteBuffer.
+ */
+ public ByteBuffer getValueAsByteBuffer() {
+ if (nla_value == null) return null;
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value);
+ // By convention, all buffers in this library are in native byte order because netlink is in
+ // native byte order. It's the order that is used by NetlinkSocket.recvMessage and the only
+ // order accepted by NetlinkMessage.parse.
+ byteBuffer.order(ByteOrder.nativeOrder());
+ return byteBuffer;
+ }
+
+ /**
+ * Get attribute value as byte.
+ */
+ public byte getValueAsByte(byte defaultValue) {
+ final ByteBuffer byteBuffer = getValueAsByteBuffer();
+ if (byteBuffer == null || byteBuffer.remaining() != Byte.BYTES) {
+ return defaultValue;
+ }
+ return getValueAsByteBuffer().get();
+ }
+
+ /**
+ * Get attribute value as Integer.
+ */
+ public int getValueAsInt(int defaultValue) {
+ final ByteBuffer byteBuffer = getValueAsByteBuffer();
+ if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
+ return defaultValue;
+ }
+ return getValueAsByteBuffer().getInt();
+ }
+
+ /**
+ * Get attribute value as InetAddress.
+ */
+ public InetAddress getValueAsInetAddress() {
+ if (nla_value == null) return null;
+
+ try {
+ return InetAddress.getByAddress(nla_value);
+ } catch (UnknownHostException ignored) {
+ return null;
+ }
+ }
+
+ /**
+ * Write the netlink attribute to {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ final ByteOrder originalOrder = byteBuffer.order();
+ final int originalPosition = byteBuffer.position();
+
+ byteBuffer.order(ByteOrder.nativeOrder());
+ try {
+ byteBuffer.putShort(nla_len);
+ byteBuffer.putShort(nla_type);
+ if (nla_value != null) byteBuffer.put(nla_value);
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
+ byteBuffer.position(originalPosition + getAlignedLength());
+ }
+
+ private void setValue(byte[] value) {
+ nla_value = value;
+ nla_len = (short) (NLA_HEADERLEN + ((nla_value != null) ? nla_value.length : 0));
+ }
+
+ @Override
+ public String toString() {
+ return "StructNlAttr{ "
+ + "nla_len{" + nla_len + "}, "
+ + "nla_type{" + nla_type + "}, "
+ + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNlMsgErr.java b/common/device/com/android/net/module/util/netlink/StructNlMsgErr.java
new file mode 100644
index 00000000..b6620f3b
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNlMsgErr.java
@@ -0,0 +1,77 @@
+/*
+ * 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.netlink;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct nlmsgerr
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/netlink.h
+ *
+ * @hide
+ */
+public class StructNlMsgErr {
+ public static final int STRUCT_SIZE = Integer.BYTES + StructNlMsgHdr.STRUCT_SIZE;
+
+ private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ /**
+ * Parse a netlink error message payload from a {@link ByteBuffer}.
+ *
+ * @param byteBuffer The buffer from which to parse the netlink error message payload.
+ * @return the parsed netlink error message payload, or {@code null} if the netlink error
+ * message payload could not be parsed successfully (for example, if it was truncated).
+ */
+ public static StructNlMsgErr parse(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) return null;
+
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the exception
+ // of usage within unittests.
+ final StructNlMsgErr struct = new StructNlMsgErr();
+ struct.error = byteBuffer.getInt();
+ struct.msg = StructNlMsgHdr.parse(byteBuffer);
+ return struct;
+ }
+
+ public int error;
+ public StructNlMsgHdr msg;
+
+ /**
+ * Write the netlink error message payload to {@link ByteBuffer}.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the possible
+ // exception of usage within unittests.
+ byteBuffer.putInt(error);
+ if (msg != null) {
+ msg.pack(byteBuffer);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StructNlMsgErr{ "
+ + "error{" + error + "}, "
+ + "msg{" + (msg == null ? "" : msg.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java b/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
new file mode 100644
index 00000000..ddf1562f
--- /dev/null
+++ b/common/device/com/android/net/module/util/netlink/StructNlMsgHdr.java
@@ -0,0 +1,173 @@
+/*
+ * 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.netlink;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.nio.ByteBuffer;
+
+/**
+ * struct nlmsghdr
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/netlink.h
+ *
+ * @hide
+ */
+public class StructNlMsgHdr {
+ // Already aligned.
+ public static final int STRUCT_SIZE = 16;
+
+ public static final short NLM_F_REQUEST = 0x0001;
+ public static final short NLM_F_MULTI = 0x0002;
+ public static final short NLM_F_ACK = 0x0004;
+ public static final short NLM_F_ECHO = 0x0008;
+ // Flags for a GET request.
+ public static final short NLM_F_ROOT = 0x0100;
+ public static final short NLM_F_MATCH = 0x0200;
+ public static final short NLM_F_DUMP = NLM_F_ROOT | NLM_F_MATCH;
+ // Flags for a NEW request.
+ public static final short NLM_F_REPLACE = 0x100;
+ public static final short NLM_F_EXCL = 0x200;
+ public static final short NLM_F_CREATE = 0x400;
+ public static final short NLM_F_APPEND = 0x800;
+
+ // TODO: Probably need to distinguish the flags which have the same value. For example,
+ // NLM_F_MATCH (0x200) and NLM_F_EXCL (0x200).
+ private static String stringForNlMsgFlags(short flags) {
+ final StringBuilder sb = new StringBuilder();
+ if ((flags & NLM_F_REQUEST) != 0) {
+ sb.append("NLM_F_REQUEST");
+ }
+ if ((flags & NLM_F_MULTI) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NLM_F_MULTI");
+ }
+ if ((flags & NLM_F_ACK) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NLM_F_ACK");
+ }
+ if ((flags & NLM_F_ECHO) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NLM_F_ECHO");
+ }
+ if ((flags & NLM_F_ROOT) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NLM_F_ROOT");
+ }
+ if ((flags & NLM_F_MATCH) != 0) {
+ if (sb.length() > 0) {
+ sb.append("|");
+ }
+ sb.append("NLM_F_MATCH");
+ }
+ return sb.toString();
+ }
+
+ private static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ /**
+ * Parse netlink message header from buffer.
+ */
+ public static StructNlMsgHdr parse(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) return null;
+
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the exception
+ // of usage within unittests.
+ final StructNlMsgHdr struct = new StructNlMsgHdr();
+ struct.nlmsg_len = byteBuffer.getInt();
+ struct.nlmsg_type = byteBuffer.getShort();
+ struct.nlmsg_flags = byteBuffer.getShort();
+ struct.nlmsg_seq = byteBuffer.getInt();
+ struct.nlmsg_pid = byteBuffer.getInt();
+
+ if (struct.nlmsg_len < STRUCT_SIZE) {
+ // Malformed.
+ return null;
+ }
+ return struct;
+ }
+
+ public int nlmsg_len;
+ public short nlmsg_type;
+ public short nlmsg_flags;
+ public int nlmsg_seq;
+ public int nlmsg_pid;
+
+ public StructNlMsgHdr() {
+ nlmsg_len = 0;
+ nlmsg_type = 0;
+ nlmsg_flags = 0;
+ nlmsg_seq = 0;
+ nlmsg_pid = 0;
+ }
+
+ /**
+ * Write netlink message header to ByteBuffer.
+ */
+ public void pack(ByteBuffer byteBuffer) {
+ // The ByteOrder must have already been set by the caller. In most
+ // cases ByteOrder.nativeOrder() is correct, with the possible
+ // exception of usage within unittests.
+ byteBuffer.putInt(nlmsg_len);
+ byteBuffer.putShort(nlmsg_type);
+ byteBuffer.putShort(nlmsg_flags);
+ byteBuffer.putInt(nlmsg_seq);
+ byteBuffer.putInt(nlmsg_pid);
+ }
+
+ @Override
+ public String toString() {
+ return toString(null /* unknown netlink family */);
+ }
+
+ /**
+ * Transform a netlink header into a string. The netlink family is required for transforming
+ * a netlink type integer into a string.
+ * @param nlFamily netlink family. Using Integer will not incur autoboxing penalties because
+ * family values are small, and all Integer objects between -128 and 127 are
+ * statically cached. See Integer.IntegerCache.
+ * @return A list of header elements.
+ */
+ @NonNull
+ public String toString(@Nullable Integer nlFamily) {
+ final String typeStr = "" + nlmsg_type
+ + "(" + (nlFamily == null
+ ? "" : NetlinkConstants.stringForNlMsgType(nlmsg_type, nlFamily))
+ + ")";
+ final String flagsStr = "" + nlmsg_flags
+ + "(" + stringForNlMsgFlags(nlmsg_flags) + ")";
+ return "StructNlMsgHdr{ "
+ + "nlmsg_len{" + nlmsg_len + "}, "
+ + "nlmsg_type{" + typeStr + "}, "
+ + "nlmsg_flags{" + flagsStr + ")}, "
+ + "nlmsg_seq{" + nlmsg_seq + "}, "
+ + "nlmsg_pid{" + nlmsg_pid + "} "
+ + "}";
+ }
+}
diff --git a/common/device/com/android/net/module/util/structs/PrefixInformationOption.java b/common/device/com/android/net/module/util/structs/PrefixInformationOption.java
index 1bdee295..49d7654d 100644
--- a/common/device/com/android/net/module/util/structs/PrefixInformationOption.java
+++ b/common/device/com/android/net/module/util/structs/PrefixInformationOption.java
@@ -18,9 +18,10 @@ package com.android.net.module.util.structs;
import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
-import android.annotation.NonNull;
import android.net.IpPrefix;
+import androidx.annotation.NonNull;
+
import com.android.net.module.util.Struct;
import com.android.net.module.util.Struct.Field;
import com.android.net.module.util.Struct.Type;