Merge "Add basic netlink library code." into mnc-dev

This commit is contained in:
Erik Kline
2015-05-13 07:04:58 +00:00
committed by Android (Google) Code Review
12 changed files with 1556 additions and 0 deletions

View 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);
}
}
}

View 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()) + "} "
+ "}";
}
}

View 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()) + "}";
}
}

View 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);
}
}

View 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: &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()) + "} "
+ "}";
}
}

View 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: &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 + "} "
+ "}";
}
}

View 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: &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 + "} "
+ "}";
}
}

View 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: &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) + "}, "
+ "}";
}
}

View 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 &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()) + "} "
+ "}";
}
}

View 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 &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 + "} "
+ "}";
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}