summaryrefslogtreecommitdiffstats
path: root/core/java/android/net/netlink
diff options
context:
space:
mode:
authorErik Kline <ek@google.com>2015-01-20 12:28:26 +0900
committerErik Kline <ek@google.com>2015-05-13 15:18:39 +0900
commit6193aa3305bc2aa5b7f0a983f4b08c99065cfb82 (patch)
tree4a6f7ebe7db140acabeb4442c401c07fa16fa46d /core/java/android/net/netlink
parent8ef4a3c9c0dd60bf8b0c959123d0afe1a62a153e (diff)
downloadframeworks_base-6193aa3305bc2aa5b7f0a983f4b08c99065cfb82.zip
frameworks_base-6193aa3305bc2aa5b7f0a983f4b08c99065cfb82.tar.gz
frameworks_base-6193aa3305bc2aa5b7f0a983f4b08c99065cfb82.tar.bz2
Add basic netlink library code.
Add netlink socket helpers and parsing code for basic netlink messages. Additionally, support from some neighbor discovery -specific messages is included. Bug: 18581716 Change-Id: Ib2aa924222b63cdbebf09a8bf8ff35ee24269fc5
Diffstat (limited to 'core/java/android/net/netlink')
-rw-r--r--core/java/android/net/netlink/NetlinkConstants.java120
-rw-r--r--core/java/android/net/netlink/NetlinkErrorMessage.java62
-rw-r--r--core/java/android/net/netlink/NetlinkMessage.java97
-rw-r--r--core/java/android/net/netlink/NetlinkSocket.java169
-rw-r--r--core/java/android/net/netlink/RtNetlinkNeighborMessage.java181
-rw-r--r--core/java/android/net/netlink/StructNdMsg.java162
-rw-r--r--core/java/android/net/netlink/StructNdaCacheInfo.java117
-rw-r--r--core/java/android/net/netlink/StructNlAttr.java127
-rw-r--r--core/java/android/net/netlink/StructNlMsgErr.java80
-rw-r--r--core/java/android/net/netlink/StructNlMsgHdr.java137
10 files changed, 1252 insertions, 0 deletions
diff --git a/core/java/android/net/netlink/NetlinkConstants.java b/core/java/android/net/netlink/NetlinkConstants.java
new file mode 100644
index 0000000..e331701
--- /dev/null
+++ b/core/java/android/net/netlink/NetlinkConstants.java
@@ -0,0 +1,120 @@
+/*
+ * 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 android.net.netlink;
+
+import android.system.OsConstants;
+import com.android.internal.util.HexDump;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * Various constants and static helper methods for netlink communications.
+ *
+ * Values taken from:
+ *
+ * &lt;linux_src&gt;/include/uapi/linux/netlink.h
+ * &lt;linux_src&gt;/include/uapi/linux/rtnetlink.h
+ *
+ * @hide
+ */
+public class NetlinkConstants {
+ private NetlinkConstants() {}
+
+ public static final int NLA_ALIGNTO = 4;
+
+ public static final int alignedLengthOf(short length) {
+ final int intLength = (int) length & 0xffff;
+ return alignedLengthOf(intLength);
+ }
+
+ public static final int alignedLengthOf(int length) {
+ if (length <= 0) { return 0; }
+ return (((length + NLA_ALIGNTO - 1) / NLA_ALIGNTO) * NLA_ALIGNTO);
+ }
+
+ 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);
+ }
+
+ public static String hexify(byte[] bytes) {
+ if (bytes == null) { return "(null)"; }
+ return HexDump.toHexString(bytes);
+ }
+
+ public static String hexify(ByteBuffer buffer) {
+ if (buffer == null) { return "(null)"; }
+ return HexDump.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;
+
+ public static String stringForNlMsgType(short nlm_type) {
+ switch (nlm_type) {
+ case NLMSG_NOOP: return "NLMSG_NOOP";
+ case NLMSG_ERROR: return "NLMSG_ERROR";
+ case NLMSG_DONE: return "NLMSG_DONE";
+ case NLMSG_OVERRUN: return "NLMSG_OVERRUN";
+ 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(nlm_type);
+ }
+ }
+}
diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/core/java/android/net/netlink/NetlinkErrorMessage.java
new file mode 100644
index 0000000..dbc10d6
--- /dev/null
+++ b/core/java/android/net/netlink/NetlinkErrorMessage.java
@@ -0,0 +1,62 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.StructNlMsgHdr;
+import android.net.netlink.NetlinkMessage;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * A NetlinkMessage subclass for netlink error messages.
+ *
+ * @hide
+ */
+public class NetlinkErrorMessage extends NetlinkMessage {
+
+ 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/core/java/android/net/netlink/NetlinkMessage.java b/core/java/android/net/netlink/NetlinkMessage.java
new file mode 100644
index 0000000..bc04a16
--- /dev/null
+++ b/core/java/android/net/netlink/NetlinkMessage.java
@@ -0,0 +1,97 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkErrorMessage;
+import android.net.netlink.RtNetlinkNeighborMessage;
+import android.net.netlink.StructNlAttr;
+import android.net.netlink.StructNlMsgHdr;
+import android.util.Log;
+
+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 final static String TAG = "NetlinkMessage";
+
+ public static NetlinkMessage parse(ByteBuffer byteBuffer) {
+ 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;
+ }
+
+ switch (nlmsghdr.nlmsg_type) {
+ //case NetlinkConstants.NLMSG_NOOP:
+ case NetlinkConstants.NLMSG_ERROR:
+ return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer);
+ case NetlinkConstants.NLMSG_DONE:
+ byteBuffer.position(byteBuffer.position() + payloadLength);
+ return new NetlinkMessage(nlmsghdr);
+ //case NetlinkConstants.NLMSG_OVERRUN:
+ case NetlinkConstants.RTM_NEWNEIGH:
+ case NetlinkConstants.RTM_DELNEIGH:
+ case NetlinkConstants.RTM_GETNEIGH:
+ return (NetlinkMessage) RtNetlinkNeighborMessage.parse(nlmsghdr, byteBuffer);
+ default:
+ if (nlmsghdr.nlmsg_type <= NetlinkConstants.NLMSG_MAX_RESERVED) {
+ // Netlink control message. Just parse the header for now,
+ // pretending the whole message was consumed.
+ byteBuffer.position(byteBuffer.position() + payloadLength);
+ return new NetlinkMessage(nlmsghdr);
+ }
+ return null;
+ }
+ }
+
+ protected StructNlMsgHdr mHeader;
+
+ public NetlinkMessage(StructNlMsgHdr nlmsghdr) {
+ mHeader = nlmsghdr;
+ }
+
+ public StructNlMsgHdr getHeader() {
+ return mHeader;
+ }
+
+ @Override
+ public String toString() {
+ return "NetlinkMessage{" + (mHeader == null ? "" : mHeader.toString()) + "}";
+ }
+}
diff --git a/core/java/android/net/netlink/NetlinkSocket.java b/core/java/android/net/netlink/NetlinkSocket.java
new file mode 100644
index 0000000..657d48c
--- /dev/null
+++ b/core/java/android/net/netlink/NetlinkSocket.java
@@ -0,0 +1,169 @@
+/*
+ * 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 android.net.netlink;
+
+import android.system.ErrnoException;
+import android.system.NetlinkSocketAddress;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructTimeval;
+import android.util.Log;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+/**
+ * NetlinkSocket
+ *
+ * A small wrapper class to assist with AF_NETLINK socket operations.
+ *
+ * @hide
+ */
+public class NetlinkSocket implements Closeable {
+ private static final String TAG = "NetlinkSocket";
+ private static final int SOCKET_RECV_BUFSIZE = 64 * 1024;
+ private static final int DEFAULT_RECV_BUFSIZE = 8 * 1024;
+
+ final private FileDescriptor mDescriptor;
+ private NetlinkSocketAddress mAddr;
+ private long mLastRecvTimeoutMs;
+ private long mLastSendTimeoutMs;
+
+ public NetlinkSocket(int nlProto) throws ErrnoException {
+ mDescriptor = Os.socket(
+ OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);
+
+ Libcore.os.setsockoptInt(
+ mDescriptor, OsConstants.SOL_SOCKET,
+ OsConstants.SO_RCVBUF, SOCKET_RECV_BUFSIZE);
+ }
+
+ public NetlinkSocketAddress getLocalAddress() throws ErrnoException {
+ return (NetlinkSocketAddress) Os.getsockname(mDescriptor);
+ }
+
+ public void bind(NetlinkSocketAddress localAddr) throws ErrnoException, SocketException {
+ Os.bind(mDescriptor, (SocketAddress)localAddr);
+ }
+
+ public void connectTo(NetlinkSocketAddress peerAddr)
+ throws ErrnoException, SocketException {
+ Os.connect(mDescriptor, (SocketAddress) peerAddr);
+ }
+
+ public void connectToKernel() throws ErrnoException, SocketException {
+ connectTo(new NetlinkSocketAddress(0, 0));
+ }
+
+ /**
+ * Wait indefinitely (or until underlying socket error) for a
+ * netlink message of at most DEFAULT_RECV_BUFSIZE size.
+ */
+ public ByteBuffer recvMessage()
+ throws ErrnoException, InterruptedIOException {
+ return recvMessage(DEFAULT_RECV_BUFSIZE, 0);
+ }
+
+ /**
+ * Wait up to |timeoutMs| (or until underlying socket error) for a
+ * netlink message of at most DEFAULT_RECV_BUFSIZE size.
+ */
+ public ByteBuffer recvMessage(long timeoutMs) throws ErrnoException, InterruptedIOException {
+ return recvMessage(DEFAULT_RECV_BUFSIZE, timeoutMs);
+ }
+
+ private 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 ByteBuffer recvMessage(int bufsize, long timeoutMs)
+ throws ErrnoException, IllegalArgumentException, InterruptedIOException {
+ checkTimeout(timeoutMs);
+
+ synchronized (mDescriptor) {
+ if (mLastRecvTimeoutMs != timeoutMs) {
+ Os.setsockoptTimeval(mDescriptor,
+ OsConstants.SOL_SOCKET, OsConstants.SO_RCVTIMEO,
+ StructTimeval.fromMillis(timeoutMs));
+ mLastRecvTimeoutMs = timeoutMs;
+ }
+ }
+
+ ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
+ int length = Os.read(mDescriptor, 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.
+ *
+ * This blocks until completion or an error occurs.
+ */
+ public boolean sendMessage(byte[] bytes, int offset, int count)
+ throws ErrnoException, InterruptedIOException {
+ return sendMessage(bytes, offset, count, 0);
+ }
+
+ /**
+ * 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 boolean sendMessage(byte[] bytes, int offset, int count, long timeoutMs)
+ throws ErrnoException, IllegalArgumentException, InterruptedIOException {
+ checkTimeout(timeoutMs);
+
+ synchronized (mDescriptor) {
+ if (mLastSendTimeoutMs != timeoutMs) {
+ Os.setsockoptTimeval(mDescriptor,
+ OsConstants.SOL_SOCKET, OsConstants.SO_SNDTIMEO,
+ StructTimeval.fromMillis(timeoutMs));
+ mLastSendTimeoutMs = timeoutMs;
+ }
+ }
+
+ return (count == Os.write(mDescriptor, bytes, offset, count));
+ }
+
+ @Override
+ public void close() {
+ IoUtils.closeQuietly(mDescriptor);
+ }
+}
diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
new file mode 100644
index 0000000..d4b572c
--- /dev/null
+++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -0,0 +1,181 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.StructNdaCacheInfo;
+import android.net.netlink.StructNdMsg;
+import android.net.netlink.StructNlAttr;
+import android.net.netlink.StructNlMsgHdr;
+import android.net.netlink.NetlinkMessage;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+/**
+ * A NetlinkMessage subclass for netlink error 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;
+
+ private static StructNlAttr findNextAttrOfType(short attrType, 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 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 = findNextAttrOfType(NDA_DST, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mDestination = nlAttr.getValueAsInetAddress();
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(NDA_LLADDR, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mLinkLayerAddr = nlAttr.nla_value;
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = findNextAttrOfType(NDA_PROBES, byteBuffer);
+ if (nlAttr != null) {
+ neighMsg.mNumProbes = nlAttr.getValueAsInt(0);
+ }
+
+ byteBuffer.position(baseOffset);
+ nlAttr = 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 = StructNlMsgHdr.NLM_F_REQUEST|StructNlMsgHdr.NLM_F_DUMP;
+ nlmsghdr.nlmsg_seq = seqNo;
+ nlmsghdr.pack(byteBuffer);
+
+ final StructNdMsg ndmsg = new StructNdMsg();
+ ndmsg.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;
+ }
+
+ @Override
+ public String toString() {
+ final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();
+ return "RtNetlinkNeighborMessage{ "
+ + "nlmsghdr{" + (mHeader == null ? "" : mHeader.toString()) + "}, "
+ + "ndmsg{" + (mNdmsg == null ? "" : mNdmsg.toString()) + "}, "
+ + "destination{" + ipLiteral + "} "
+ + "linklayeraddr{" + NetlinkConstants.hexify(mLinkLayerAddr) + "} "
+ + "probes{" + mNumProbes + "} "
+ + "cacheinfo{" + (mCacheInfo == null ? "" : mCacheInfo.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java
new file mode 100644
index 0000000..e66d45d
--- /dev/null
+++ b/core/java/android/net/netlink/StructNdMsg.java
@@ -0,0 +1,162 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.NetlinkConstants;
+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_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;
+
+ public static String stringForNudState(short nudState) {
+ switch (nudState) {
+ 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);
+ }
+ }
+
+ public static boolean isNudStateConnected(short nudState) {
+ return ((nudState & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE)) != 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;
+
+ public 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;
+ }
+
+ 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;
+ }
+
+ public boolean pack(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) { return false; }
+
+ // 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);
+ return true;
+ }
+
+ public boolean nudConnected() {
+ return isNudStateConnected(ndm_state);
+ }
+
+ public boolean nudValid() {
+ return (nudConnected() || ((ndm_state & (NUD_PROBE|NUD_STALE|NUD_DELAY)) != 0));
+ }
+
+ @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/core/java/android/net/netlink/StructNdaCacheInfo.java b/core/java/android/net/netlink/StructNdaCacheInfo.java
new file mode 100644
index 0000000..16cf563
--- /dev/null
+++ b/core/java/android/net/netlink/StructNdaCacheInfo.java
@@ -0,0 +1,117 @@
+/*
+ * 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 android.net.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;
+ }
+
+ 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() {}
+
+ public long lastUsed() {
+ return ticksToMilliSeconds(ndm_used);
+ }
+
+ public long lastConfirmed() {
+ return ticksToMilliSeconds(ndm_confirmed);
+ }
+
+ 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/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java
new file mode 100644
index 0000000..9aef4c7
--- /dev/null
+++ b/core/java/android/net/netlink/StructNlAttr.java
@@ -0,0 +1,127 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.NetlinkConstants;
+import libcore.io.SizeOf;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+
+
+/**
+ * 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;
+
+ // 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();
+ struct.nla_len = byteBuffer.getShort();
+ struct.nla_type = byteBuffer.getShort();
+ struct.mByteOrder = byteBuffer.order();
+
+ byteBuffer.position(baseOffset);
+ if (struct.nla_len < NLA_HEADERLEN) {
+ // Malformed.
+ return null;
+ }
+ return struct;
+ }
+
+ 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;
+ }
+
+ public short nla_len;
+ public short nla_type;
+ public byte[] nla_value;
+ public ByteOrder mByteOrder;
+
+ public StructNlAttr() {
+ mByteOrder = ByteOrder.nativeOrder();
+ }
+
+ public int getAlignedLength() {
+ return NetlinkConstants.alignedLengthOf(nla_len);
+ }
+
+ public ByteBuffer getValueAsByteBuffer() {
+ if (nla_value == null) { return null; }
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(nla_value);
+ byteBuffer.order(mByteOrder);
+ return byteBuffer;
+ }
+
+ public int getValueAsInt(int defaultValue) {
+ final ByteBuffer byteBuffer = getValueAsByteBuffer();
+ if (byteBuffer == null || byteBuffer.remaining() != SizeOf.INT) {
+ return defaultValue;
+ }
+ return getValueAsByteBuffer().getInt();
+ }
+
+ public InetAddress getValueAsInetAddress() {
+ if (nla_value == null) { return null; }
+
+ try {
+ return InetAddress.getByAddress(nla_value);
+ } catch (UnknownHostException ignored) {
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "StructNlAttr{ "
+ + "nla_len{" + nla_len + "}, "
+ + "nla_type{" + nla_type + "}, "
+ + "nla_value{" + NetlinkConstants.hexify(nla_value) + "}, "
+ + "}";
+ }
+}
diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java
new file mode 100644
index 0000000..5da19a2
--- /dev/null
+++ b/core/java/android/net/netlink/StructNlMsgErr.java
@@ -0,0 +1,80 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.NetlinkConstants;
+import android.net.netlink.StructNlMsgHdr;
+import libcore.io.SizeOf;
+
+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 = SizeOf.INT + StructNlMsgHdr.STRUCT_SIZE;
+
+ public static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ 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;
+
+ public StructNlMsgErr() {
+ error = 0;
+ msg = null;
+ }
+
+ public boolean pack(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) { return false; }
+
+ // 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);
+ }
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "StructNlMsgErr{ "
+ + "error{" + error + "}, "
+ + "msg{" + (msg == null ? "" : msg.toString()) + "} "
+ + "}";
+ }
+}
diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java
new file mode 100644
index 0000000..67925ac
--- /dev/null
+++ b/core/java/android/net/netlink/StructNlMsgHdr.java
@@ -0,0 +1,137 @@
+/*
+ * 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 android.net.netlink;
+
+import android.net.netlink.NetlinkConstants;
+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;
+
+ public 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();
+ }
+
+ public static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
+ return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
+ }
+
+ 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;
+ }
+
+ public boolean pack(ByteBuffer byteBuffer) {
+ if (!hasAvailableSpace(byteBuffer)) { return false; }
+
+ // 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);
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ final String typeStr = "" + nlmsg_type
+ + "(" + NetlinkConstants.stringForNlMsgType(nlmsg_type) + ")";
+ 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 + "} "
+ + "}";
+ }
+}