Merge "Add IpReachabilityMonitor#probeAll() to begin doing DNAv4/v6-like probing" into mnc-dev

This commit is contained in:
Erik Kline
2015-05-19 02:36:53 +00:00
committed by Android (Google) Code Review
7 changed files with 178 additions and 22 deletions

View File

@ -75,7 +75,6 @@ public class IpReachabilityMonitor {
private boolean mRunning;
final private Thread mObserverThread;
// TODO: consider passing in a NetworkInterface object from the caller.
public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException {
mInterfaceName = ifName;
int ifIndex = -1;
@ -154,6 +153,12 @@ public class IpReachabilityMonitor {
}
}
private boolean stillRunning() {
synchronized (mLock) {
return mRunning;
}
}
public void updateLinkProperties(LinkProperties lp) {
if (!mInterfaceName.equals(lp.getInterfaceName())) {
// TODO: figure out how to cope with interface changes.
@ -204,6 +209,47 @@ public class IpReachabilityMonitor {
}
}
public void probeAll() {
Set<InetAddress> ipProbeList = new HashSet<InetAddress>();
synchronized (mLock) {
ipProbeList.addAll(mIpWatchList);
}
for (InetAddress target : ipProbeList) {
if (!stillRunning()) { break; }
probeIp(target);
}
}
private void probeIp(InetAddress ip) {
// This currently does not cause neighbor probing if the target |ip|
// has been confirmed reachable within the past "delay_probe_time"
// seconds, i.e. within the past 5 seconds.
//
// TODO: replace with a transition directly to NUD_PROBE state once
// kernels are updated to do so correctly.
if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); }
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
1, ip, StructNdMsg.NUD_DELAY, mInterfaceIndex, null);
NetlinkSocket nlSocket = null;
try {
nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE);
nlSocket.connectToKernel();
nlSocket.sendMessage(msg, 0, msg.length, 300);
final NetlinkMessage response = NetlinkMessage.parse(nlSocket.recvMessage(300));
if (response != null && response instanceof NetlinkErrorMessage) {
Log.e(TAG, "Error probing ip=" + response.toString());
}
} catch (ErrnoException | InterruptedIOException | SocketException e) {
Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e);
}
if (nlSocket != null) {
nlSocket.close();
}
}
private final class NetlinkSocketObserver implements Runnable {
private static final String TAG = "NetlinkSocketObserver";
@ -242,12 +288,6 @@ public class IpReachabilityMonitor {
if (VDBG) { Log.d(TAG, "Finishing observing thread."); }
}
private boolean stillRunning() {
synchronized (mLock) {
return mRunning;
}
}
private void clearNetlinkSocket() {
if (mSocket != null) {
mSocket.close();

View File

@ -21,9 +21,11 @@ import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlAttr;
import android.net.netlink.StructNlMsgHdr;
import android.net.netlink.NetlinkMessage;
import android.system.OsConstants;
import android.util.Log;
import java.net.InetAddress;
import java.net.Inet6Address;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@ -131,6 +133,34 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
return bytes;
}
/**
* A convenience method to create an RTM_NEWNEIGH message, to modify
* the kernel's state information for a specific neighbor.
*/
public static byte[] newNewNeighborMessage(
int seqNo, InetAddress ip, short nudState, int ifIndex, byte[] llAddr) {
final StructNlMsgHdr nlmsghdr = new StructNlMsgHdr();
nlmsghdr.nlmsg_type = NetlinkConstants.RTM_NEWNEIGH;
nlmsghdr.nlmsg_flags = StructNlMsgHdr.NLM_F_REQUEST | StructNlMsgHdr.NLM_F_REPLACE;
nlmsghdr.nlmsg_seq = seqNo;
final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr);
msg.mNdmsg = new StructNdMsg();
msg.mNdmsg.ndm_family =
(byte) ((ip instanceof Inet6Address) ? OsConstants.AF_INET6 : OsConstants.AF_INET);
msg.mNdmsg.ndm_ifindex = ifIndex;
msg.mNdmsg.ndm_state = nudState;
msg.mDestination = ip;
msg.mLinkLayerAddr = llAddr; // might be null
final byte[] bytes = new byte[msg.getRequiredSpace()];
nlmsghdr.nlmsg_len = bytes.length;
final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
byteBuffer.order(ByteOrder.nativeOrder());
msg.pack(byteBuffer);
return bytes;
}
private StructNdMsg mNdmsg;
private InetAddress mDestination;
private byte[] mLinkLayerAddr;
@ -166,6 +196,41 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage {
return mCacheInfo;
}
public int getRequiredSpace() {
int spaceRequired = StructNlMsgHdr.STRUCT_SIZE + StructNdMsg.STRUCT_SIZE;
if (mDestination != null) {
spaceRequired += NetlinkConstants.alignedLengthOf(
StructNlAttr.NLA_HEADERLEN + mDestination.getAddress().length);
}
if (mLinkLayerAddr != null) {
spaceRequired += NetlinkConstants.alignedLengthOf(
StructNlAttr.NLA_HEADERLEN + mLinkLayerAddr.length);
}
// Currently we don't write messages with NDA_PROBES nor NDA_CACHEINFO
// attributes appended. Fix later, if necessary.
return spaceRequired;
}
private static void packNlAttr(short nlType, byte[] nlValue, ByteBuffer byteBuffer) {
final StructNlAttr nlAttr = new StructNlAttr();
nlAttr.nla_type = nlType;
nlAttr.nla_value = nlValue;
nlAttr.nla_len = (short) (StructNlAttr.NLA_HEADERLEN + nlAttr.nla_value.length);
nlAttr.pack(byteBuffer);
}
public void pack(ByteBuffer byteBuffer) {
getHeader().pack(byteBuffer) ;
mNdmsg.pack(byteBuffer);
if (mDestination != null) {
packNlAttr(NDA_DST, mDestination.getAddress(), byteBuffer);
}
if (mLinkLayerAddr != null) {
packNlAttr(NDA_LLADDR, mLinkLayerAddr, byteBuffer);
}
}
@Override
public String toString() {
final String ipLiteral = (mDestination == null) ? "" : mDestination.getHostAddress();

View File

@ -123,9 +123,7 @@ public class StructNdMsg {
ndm_family = (byte) OsConstants.AF_UNSPEC;
}
public boolean pack(ByteBuffer byteBuffer) {
if (!hasAvailableSpace(byteBuffer)) { return false; }
public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the exception
// of usage within unittests.
@ -136,7 +134,6 @@ public class StructNdMsg {
byteBuffer.putShort(ndm_state);
byteBuffer.put(ndm_flags);
byteBuffer.put(ndm_type);
return true;
}
public boolean nudConnected() {

View File

@ -116,6 +116,14 @@ public class StructNlAttr {
}
}
public void pack(ByteBuffer byteBuffer) {
final int originalPosition = byteBuffer.position();
byteBuffer.putShort(nla_len);
byteBuffer.putShort(nla_type);
byteBuffer.put(nla_value);
byteBuffer.position(originalPosition + getAlignedLength());
}
@Override
public String toString() {
return "StructNlAttr{ "

View File

@ -57,9 +57,7 @@ public class StructNlMsgErr {
msg = null;
}
public boolean pack(ByteBuffer byteBuffer) {
if (!hasAvailableSpace(byteBuffer)) { return false; }
public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the possible
// exception of usage within unittests.
@ -67,7 +65,6 @@ public class StructNlMsgErr {
if (msg != null) {
msg.pack(byteBuffer);
}
return true;
}
@Override

View File

@ -39,6 +39,12 @@ public class StructNlMsgHdr {
public static final short NLM_F_ROOT = 0x0100;
public static final short NLM_F_MATCH = 0x0200;
public static final short NLM_F_DUMP = NLM_F_ROOT|NLM_F_MATCH;
// Flags for a NEW request.
public static final short NLM_F_REPLACE = 0x100;
public static final short NLM_F_EXCL = 0x200;
public static final short NLM_F_CREATE = 0x400;
public static final short NLM_F_APPEND = 0x800;
public static String stringForNlMsgFlags(short flags) {
final StringBuilder sb = new StringBuilder();
@ -106,9 +112,7 @@ public class StructNlMsgHdr {
nlmsg_pid = 0;
}
public boolean pack(ByteBuffer byteBuffer) {
if (!hasAvailableSpace(byteBuffer)) { return false; }
public void pack(ByteBuffer byteBuffer) {
// The ByteOrder must have already been set by the caller. In most
// cases ByteOrder.nativeOrder() is correct, with the possible
// exception of usage within unittests.
@ -117,7 +121,6 @@ public class StructNlMsgHdr {
byteBuffer.putShort(nlmsg_flags);
byteBuffer.putInt(nlmsg_seq);
byteBuffer.putInt(nlmsg_pid);
return true;
}
@Override

View File

@ -26,9 +26,11 @@ import android.util.Log;
import libcore.util.HexEncoding;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
import junit.framework.TestCase;
@ -133,7 +135,7 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
public static final byte[] RTM_GETNEIGH_RESPONSE =
HexEncoding.decode(RTM_GETNEIGH_RESPONSE_HEX.replaceAll(" ", "").toCharArray(), false);
public void testParseRtNetlinkNeighborRtmDelNeigh() {
public void testParseRtmDelNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_DELNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@ -159,7 +161,7 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
assertEquals(InetAddress.parseNumericAddress("192.168.159.254"), destination);
}
public void testParseRtNetlinkNeighborRtmNewNeigh() {
public void testParseRtmNewNeigh() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_NEWNEIGH);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer);
@ -185,7 +187,7 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
assertEquals(InetAddress.parseNumericAddress("fe80::86c9:b2ff:fe6a:ed4b"), destination);
}
public void testParseRtNetlinkNeighborRtmGetNeighResponse() {
public void testParseRtmGetNeighResponse() {
final ByteBuffer byteBuffer = ByteBuffer.wrap(RTM_GETNEIGH_RESPONSE);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing.
@ -208,4 +210,48 @@ public class RtNetlinkNeighborMessageTest extends TestCase {
// TODO: add more detailed spot checks.
assertEquals(14, messageCount);
}
public void testCreateRtmNewNeighMessage() {
final int seqNo = 2635;
final int ifIndex = 14;
final byte[] llAddr =
new byte[] { (byte) 1, (byte) 2, (byte) 3, (byte) 4, (byte) 5, (byte) 6 };
// Hexadecimal representation of our created packet.
final String expectedNewNeighHex =
// struct nlmsghdr
"30000000" + // length = 48
"1c00" + // type = 28 (RTM_NEWNEIGH)
"0101" + // flags (NLM_F_REQUEST | NLM_F_REPLACE)
"4b0a0000" + // seqno
"00000000" + // pid (0 == kernel)
// struct ndmsg
"02" + // family
"00" + // pad1
"0000" + // pad2
"0e000000" + // interface index (14)
"0800" + // NUD state (0x08 == NUD_DELAY)
"00" + // flags
"00" + // type
// struct nlattr: NDA_DST
"0800" + // length = 8
"0100" + // type (1 == NDA_DST, for neighbor messages)
"7f000001" + // IPv4 address (== 127.0.0.1)
// struct nlattr: NDA_LLADDR
"0a00" + // length = 10
"0200" + // type (2 == NDA_LLADDR, for neighbor messages)
"010203040506" + // MAC Address (== 01:02:03:04:05:06)
"0000"; // padding, for 4 byte alignment
final byte[] expectedNewNeigh =
HexEncoding.decode(expectedNewNeighHex.toCharArray(), false);
final byte[] bytes = RtNetlinkNeighborMessage.newNewNeighborMessage(
seqNo, Inet4Address.LOOPBACK, StructNdMsg.NUD_DELAY, ifIndex, llAddr);
if (!Arrays.equals(expectedNewNeigh, bytes)) {
assertEquals(expectedNewNeigh.length, bytes.length);
for (int i = 0; i < Math.min(expectedNewNeigh.length, bytes.length); i++) {
assertEquals(expectedNewNeigh[i], bytes[i]);
}
}
}
}