* commit '9a3cdadb4cb79a3ab0e2a9ea3ba2365b5942ca34': Add basic netlink library code.
This commit is contained in:
120
core/java/android/net/netlink/NetlinkConstants.java
Normal file
120
core/java/android/net/netlink/NetlinkConstants.java
Normal file
@@ -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:
|
||||||
|
*
|
||||||
|
* <linux_src>/include/uapi/linux/netlink.h
|
||||||
|
* <linux_src>/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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
core/java/android/net/netlink/NetlinkErrorMessage.java
Normal file
62
core/java/android/net/netlink/NetlinkErrorMessage.java
Normal file
@@ -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()) + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
97
core/java/android/net/netlink/NetlinkMessage.java
Normal file
97
core/java/android/net/netlink/NetlinkMessage.java
Normal file
@@ -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()) + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
169
core/java/android/net/netlink/NetlinkSocket.java
Normal file
169
core/java/android/net/netlink/NetlinkSocket.java
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
181
core/java/android/net/netlink/RtNetlinkNeighborMessage.java
Normal file
181
core/java/android/net/netlink/RtNetlinkNeighborMessage.java
Normal file
@@ -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: <linux_src>/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()) + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
162
core/java/android/net/netlink/StructNdMsg.java
Normal file
162
core/java/android/net/netlink/StructNdMsg.java
Normal file
@@ -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: <linux_src>/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 + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
117
core/java/android/net/netlink/StructNdaCacheInfo.java
Normal file
117
core/java/android/net/netlink/StructNdaCacheInfo.java
Normal file
@@ -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: <linux_src>/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 + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
127
core/java/android/net/netlink/StructNlAttr.java
Normal file
127
core/java/android/net/netlink/StructNlAttr.java
Normal file
@@ -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: <linux_src>/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) + "}, "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
80
core/java/android/net/netlink/StructNlMsgErr.java
Normal file
80
core/java/android/net/netlink/StructNlMsgErr.java
Normal file
@@ -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 <linux_src>/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()) + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
137
core/java/android/net/netlink/StructNlMsgHdr.java
Normal file
137
core/java/android/net/netlink/StructNlMsgHdr.java
Normal file
@@ -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 <linux_src>/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 + "} "
|
||||||
|
+ "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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.NetlinkSocket;
|
||||||
|
import android.net.netlink.RtNetlinkNeighborMessage;
|
||||||
|
import android.net.netlink.StructNdMsg;
|
||||||
|
import android.net.netlink.StructNlMsgHdr;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.NetlinkSocketAddress;
|
||||||
|
import android.system.OsConstants;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
public class NetlinkSocketTest extends TestCase {
|
||||||
|
private final String TAG = "NetlinkSocketTest";
|
||||||
|
|
||||||
|
public void testBasicWorkingGetNeighborsQuery() throws Exception {
|
||||||
|
NetlinkSocket s = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
|
||||||
|
assertNotNull(s);
|
||||||
|
|
||||||
|
s.connectToKernel();
|
||||||
|
|
||||||
|
NetlinkSocketAddress localAddr = s.getLocalAddress();
|
||||||
|
assertNotNull(localAddr);
|
||||||
|
assertEquals(0, localAddr.getGroupsMask());
|
||||||
|
assertTrue(0 != localAddr.getPortId());
|
||||||
|
|
||||||
|
final int TEST_SEQNO = 5;
|
||||||
|
final byte[] request = RtNetlinkNeighborMessage.newGetNeighborsRequest(TEST_SEQNO);
|
||||||
|
assertNotNull(request);
|
||||||
|
|
||||||
|
final long TIMEOUT = 500;
|
||||||
|
assertTrue(s.sendMessage(request, 0, request.length, TIMEOUT));
|
||||||
|
|
||||||
|
int neighMessageCount = 0;
|
||||||
|
int doneMessageCount = 0;
|
||||||
|
|
||||||
|
while (doneMessageCount == 0) {
|
||||||
|
ByteBuffer response = null;
|
||||||
|
response = s.recvMessage(TIMEOUT);
|
||||||
|
assertNotNull(response);
|
||||||
|
assertTrue(StructNlMsgHdr.STRUCT_SIZE <= response.limit());
|
||||||
|
assertEquals(0, response.position());
|
||||||
|
assertEquals(ByteOrder.nativeOrder(), response.order());
|
||||||
|
|
||||||
|
// Verify the messages at least appears minimally reasonable.
|
||||||
|
while (response.remaining() > 0) {
|
||||||
|
final NetlinkMessage msg = NetlinkMessage.parse(response);
|
||||||
|
assertNotNull(msg);
|
||||||
|
final StructNlMsgHdr hdr = msg.getHeader();
|
||||||
|
assertNotNull(hdr);
|
||||||
|
|
||||||
|
if (hdr.nlmsg_type == NetlinkConstants.NLMSG_DONE) {
|
||||||
|
doneMessageCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(NetlinkConstants.RTM_NEWNEIGH, hdr.nlmsg_type);
|
||||||
|
assertTrue(msg instanceof RtNetlinkNeighborMessage);
|
||||||
|
assertTrue((hdr.nlmsg_flags & StructNlMsgHdr.NLM_F_MULTI) != 0);
|
||||||
|
assertEquals(TEST_SEQNO, hdr.nlmsg_seq);
|
||||||
|
assertEquals(localAddr.getPortId(), hdr.nlmsg_pid);
|
||||||
|
|
||||||
|
neighMessageCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(1, doneMessageCount);
|
||||||
|
// TODO: make sure this test passes sanely in airplane mode.
|
||||||
|
assertTrue(neighMessageCount > 0);
|
||||||
|
|
||||||
|
s.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,211 @@
|
|||||||
|
/*
|
||||||
|
* 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.NetlinkMessage;
|
||||||
|
import android.net.netlink.RtNetlinkNeighborMessage;
|
||||||
|
import android.net.netlink.StructNdMsg;
|
||||||
|
import android.net.netlink.StructNlMsgHdr;
|
||||||
|
import android.system.OsConstants;
|
||||||
|
import android.util.Log;
|
||||||
|
import libcore.util.HexEncoding;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
|
||||||
|
public class RtNetlinkNeighborMessageTest extends TestCase {
|
||||||
|
private final String TAG = "RtNetlinkNeighborMessageTest";
|
||||||
|
|
||||||
|
// Hexadecimal representation of packet capture.
|
||||||
|
public static final String RTM_DELNEIGH_HEX =
|
||||||
|
// struct nlmsghdr
|
||||||
|
"4c000000" + // length = 76
|
||||||
|
"1d00" + // type = 29 (RTM_DELNEIGH)
|
||||||
|
"0000" + // flags
|
||||||
|
"00000000" + // seqno
|
||||||
|
"00000000" + // pid (0 == kernel)
|
||||||
|
// struct ndmsg
|
||||||
|
"02" + // family
|
||||||
|
"00" + // pad1
|
||||||
|
"0000" + // pad2
|
||||||
|
"15000000" + // interface index (21 == wlan0, on test device)
|
||||||
|
"0400" + // NUD state (0x04 == NUD_STALE)
|
||||||
|
"00" + // flags
|
||||||
|
"01" + // type
|
||||||
|
// struct nlattr: NDA_DST
|
||||||
|
"0800" + // length = 8
|
||||||
|
"0100" + // type (1 == NDA_DST, for neighbor messages)
|
||||||
|
"c0a89ffe" + // IPv4 address (== 192.168.159.254)
|
||||||
|
// struct nlattr: NDA_LLADDR
|
||||||
|
"0a00" + // length = 10
|
||||||
|
"0200" + // type (2 == NDA_LLADDR, for neighbor messages)
|
||||||
|
"00005e000164" + // MAC Address (== 00:00:5e:00:01:64)
|
||||||
|
"0000" + // padding, for 4 byte alignment
|
||||||
|
// struct nlattr: NDA_PROBES
|
||||||
|
"0800" + // length = 8
|
||||||
|
"0400" + // type (4 == NDA_PROBES, for neighbor messages)
|
||||||
|
"01000000" + // number of probes
|
||||||
|
// struct nlattr: NDA_CACHEINFO
|
||||||
|
"1400" + // length = 20
|
||||||
|
"0300" + // type (3 == NDA_CACHEINFO, for neighbor messages)
|
||||||
|
"05190000" + // ndm_used, as "clock ticks ago"
|
||||||
|
"05190000" + // ndm_confirmed, as "clock ticks ago"
|
||||||
|
"190d0000" + // ndm_updated, as "clock ticks ago"
|
||||||
|
"00000000"; // ndm_refcnt
|
||||||
|
public static final byte[] RTM_DELNEIGH =
|
||||||
|
HexEncoding.decode(RTM_DELNEIGH_HEX.toCharArray(), false);
|
||||||
|
|
||||||
|
// Hexadecimal representation of packet capture.
|
||||||
|
public static final String RTM_NEWNEIGH_HEX =
|
||||||
|
// struct nlmsghdr
|
||||||
|
"58000000" + // length = 88
|
||||||
|
"1c00" + // type = 28 (RTM_NEWNEIGH)
|
||||||
|
"0000" + // flags
|
||||||
|
"00000000" + // seqno
|
||||||
|
"00000000" + // pid (0 == kernel)
|
||||||
|
// struct ndmsg
|
||||||
|
"0a" + // family
|
||||||
|
"00" + // pad1
|
||||||
|
"0000" + // pad2
|
||||||
|
"15000000" + // interface index (21 == wlan0, on test device)
|
||||||
|
"0400" + // NUD state (0x04 == NUD_STALE)
|
||||||
|
"80" + // flags
|
||||||
|
"01" + // type
|
||||||
|
// struct nlattr: NDA_DST
|
||||||
|
"1400" + // length = 20
|
||||||
|
"0100" + // type (1 == NDA_DST, for neighbor messages)
|
||||||
|
"fe8000000000000086c9b2fffe6aed4b" + // IPv6 address (== fe80::86c9:b2ff:fe6a:ed4b)
|
||||||
|
// struct nlattr: NDA_LLADDR
|
||||||
|
"0a00" + // length = 10
|
||||||
|
"0200" + // type (2 == NDA_LLADDR, for neighbor messages)
|
||||||
|
"84c9b26aed4b" + // MAC Address (== 84:c9:b2:6a:ed:4b)
|
||||||
|
"0000" + // padding, for 4 byte alignment
|
||||||
|
// struct nlattr: NDA_PROBES
|
||||||
|
"0800" + // length = 8
|
||||||
|
"0400" + // type (4 == NDA_PROBES, for neighbor messages)
|
||||||
|
"01000000" + // number of probes
|
||||||
|
// struct nlattr: NDA_CACHEINFO
|
||||||
|
"1400" + // length = 20
|
||||||
|
"0300" + // type (3 == NDA_CACHEINFO, for neighbor messages)
|
||||||
|
"eb0e0000" + // ndm_used, as "clock ticks ago"
|
||||||
|
"861f0000" + // ndm_confirmed, as "clock ticks ago"
|
||||||
|
"00000000" + // ndm_updated, as "clock ticks ago"
|
||||||
|
"05000000"; // ndm_refcnt
|
||||||
|
public static final byte[] RTM_NEWNEIGH =
|
||||||
|
HexEncoding.decode(RTM_NEWNEIGH_HEX.toCharArray(), false);
|
||||||
|
|
||||||
|
// An example of the full response from an RTM_GETNEIGH query.
|
||||||
|
private static final String RTM_GETNEIGH_RESPONSE_HEX =
|
||||||
|
// <-- struct nlmsghr -->|<-- struct ndmsg -->|<-- struct nlattr: NDA_DST -->|<-- NDA_LLADDR -->|<-- NDA_PROBES -->|<-- NDA_CACHEINFO -->|
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff020000000000000000000000000001 0a00 0200 333300000001 0000 0800 0400 00000000 1400 0300 a2280000 32110000 32110000 01000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff0200000000000000000001ff000001 0a00 0200 3333ff000001 0000 0800 0400 00000000 1400 0300 0d280000 9d100000 9d100000 00000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 0400 80 01 1400 0100 20010db800040ca00000000000000001 0a00 0200 84c9b26aed4b 0000 0800 0400 04000000 1400 0300 90100000 90100000 90080000 01000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff0200000000000000000001ff47da19 0a00 0200 3333ff47da19 0000 0800 0400 00000000 1400 0300 a1280000 31110000 31110000 01000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 14000000 4000 00 05 1400 0100 ff020000000000000000000000000016 0a00 0200 333300000016 0000 0800 0400 00000000 1400 0300 912a0000 21130000 21130000 00000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 14000000 4000 00 05 1400 0100 ff0200000000000000000001ffeace3b 0a00 0200 3333ffeace3b 0000 0800 0400 00000000 1400 0300 922a0000 22130000 22130000 00000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff0200000000000000000001ff5c2a83 0a00 0200 3333ff5c2a83 0000 0800 0400 00000000 1400 0300 391c0000 c9040000 c9040000 01000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 01000000 4000 00 02 1400 0100 00000000000000000000000000000000 0a00 0200 000000000000 0000 0800 0400 00000000 1400 0300 cd180200 5d010200 5d010200 08000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff020000000000000000000000000002 0a00 0200 333300000002 0000 0800 0400 00000000 1400 0300 352a0000 c5120000 c5120000 00000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff020000000000000000000000000016 0a00 0200 333300000016 0000 0800 0400 00000000 1400 0300 982a0000 28130000 28130000 00000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 0800 80 01 1400 0100 fe8000000000000086c9b2fffe6aed4b 0a00 0200 84c9b26aed4b 0000 0800 0400 00000000 1400 0300 23000000 24000000 57000000 13000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 15000000 4000 00 05 1400 0100 ff0200000000000000000001ffeace3b 0a00 0200 3333ffeace3b 0000 0800 0400 00000000 1400 0300 992a0000 29130000 29130000 01000000" +
|
||||||
|
"58000000 1c00 0200 00000000 3e2b0000 0a 00 0000 14000000 4000 00 05 1400 0100 ff020000000000000000000000000002 0a00 0200 333300000002 0000 0800 0400 00000000 1400 0300 2e2a0000 be120000 be120000 00000000" +
|
||||||
|
"44000000 1c00 0200 00000000 3e2b0000 02 00 0000 18000000 4000 00 03 0800 0100 00000000 0400 0200 0800 0400 00000000 1400 0300 75280000 05110000 05110000 22000000";
|
||||||
|
public static final byte[] RTM_GETNEIGH_RESPONSE =
|
||||||
|
HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
|
||||||
|
|
||||||
|
public void testParseRtNetlinkNeighborRtmDelNeigh() {
|
||||||
|
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
|
||||||
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
|
||||||
|
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
|
||||||
|
assertNotNull(msg);
|
||||||
|
assertTrue(msg instanceof RtNetlinkNeighborMessage);
|
||||||
|
final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) msg;
|
||||||
|
|
||||||
|
final StructNlMsgHdr hdr = neighMsg.getHeader();
|
||||||
|
assertNotNull(hdr);
|
||||||
|
assertEquals(76, hdr.nlmsg_len);
|
||||||
|
assertEquals(NetlinkConstants.RTM_DELNEIGH, hdr.nlmsg_type);
|
||||||
|
assertEquals(0, hdr.nlmsg_flags);
|
||||||
|
assertEquals(0, hdr.nlmsg_seq);
|
||||||
|
assertEquals(0, hdr.nlmsg_pid);
|
||||||
|
|
||||||
|
final StructNdMsg ndmsgHdr = neighMsg.getNdHeader();
|
||||||
|
assertNotNull(ndmsgHdr);
|
||||||
|
assertEquals((byte) OsConstants.AF_INET, ndmsgHdr.ndm_family);
|
||||||
|
assertEquals(21, ndmsgHdr.ndm_ifindex);
|
||||||
|
assertEquals(StructNdMsg.NUD_STALE, ndmsgHdr.ndm_state);
|
||||||
|
final InetAddress destination = neighMsg.getDestination();
|
||||||
|
assertNotNull(destination);
|
||||||
|
assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseRtNetlinkNeighborRtmNewNeigh() {
|
||||||
|
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
|
||||||
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
|
||||||
|
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
|
||||||
|
assertNotNull(msg);
|
||||||
|
assertTrue(msg instanceof RtNetlinkNeighborMessage);
|
||||||
|
final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) msg;
|
||||||
|
|
||||||
|
final StructNlMsgHdr hdr = neighMsg.getHeader();
|
||||||
|
assertNotNull(hdr);
|
||||||
|
assertEquals(88, hdr.nlmsg_len);
|
||||||
|
assertEquals(NetlinkConstants.RTM_NEWNEIGH, hdr.nlmsg_type);
|
||||||
|
assertEquals(0, hdr.nlmsg_flags);
|
||||||
|
assertEquals(0, hdr.nlmsg_seq);
|
||||||
|
assertEquals(0, hdr.nlmsg_pid);
|
||||||
|
|
||||||
|
final StructNdMsg ndmsgHdr = neighMsg.getNdHeader();
|
||||||
|
assertNotNull(ndmsgHdr);
|
||||||
|
assertEquals((byte) OsConstants.AF_INET6, ndmsgHdr.ndm_family);
|
||||||
|
assertEquals(21, ndmsgHdr.ndm_ifindex);
|
||||||
|
assertEquals(StructNdMsg.NUD_STALE, ndmsgHdr.ndm_state);
|
||||||
|
final InetAddress destination = neighMsg.getDestination();
|
||||||
|
assertNotNull(destination);
|
||||||
|
assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testParseRtNetlinkNeighborRtmGetNeighResponse() {
|
||||||
|
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
|
||||||
|
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
|
||||||
|
|
||||||
|
int messageCount = 0;
|
||||||
|
while (byteBuffer.remaining() > 0) {
|
||||||
|
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
|
||||||
|
assertNotNull(msg);
|
||||||
|
assertTrue(msg instanceof RtNetlinkNeighborMessage);
|
||||||
|
final RtNetlinkNeighborMessage neighMsg = (RtNetlinkNeighborMessage) msg;
|
||||||
|
|
||||||
|
final StructNlMsgHdr hdr = neighMsg.getHeader();
|
||||||
|
assertNotNull(hdr);
|
||||||
|
assertEquals(NetlinkConstants.RTM_NEWNEIGH, hdr.nlmsg_type);
|
||||||
|
assertEquals(StructNlMsgHdr.NLM_F_MULTI, hdr.nlmsg_flags);
|
||||||
|
assertEquals(0, hdr.nlmsg_seq);
|
||||||
|
assertEquals(11070, hdr.nlmsg_pid);
|
||||||
|
|
||||||
|
messageCount++;
|
||||||
|
}
|
||||||
|
// TODO: add more detailed spot checks.
|
||||||
|
assertEquals(14, messageCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user