diff options
7 files changed, 178 insertions, 22 deletions
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java index 7e1c05b..add8774 100644 --- a/core/java/android/net/IpReachabilityMonitor.java +++ b/core/java/android/net/IpReachabilityMonitor.java @@ -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(); diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java index d4b572c..b5f5817 100644 --- a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java +++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java @@ -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(); diff --git a/core/java/android/net/netlink/StructNdMsg.java b/core/java/android/net/netlink/StructNdMsg.java index e66d45d..ca1cbbb 100644 --- a/core/java/android/net/netlink/StructNdMsg.java +++ b/core/java/android/net/netlink/StructNdMsg.java @@ -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() { diff --git a/core/java/android/net/netlink/StructNlAttr.java b/core/java/android/net/netlink/StructNlAttr.java index 9aef4c7..597a6aa 100644 --- a/core/java/android/net/netlink/StructNlAttr.java +++ b/core/java/android/net/netlink/StructNlAttr.java @@ -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{ " diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java index 5da19a2..6b02650 100644 --- a/core/java/android/net/netlink/StructNlMsgErr.java +++ b/core/java/android/net/netlink/StructNlMsgErr.java @@ -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 diff --git a/core/java/android/net/netlink/StructNlMsgHdr.java b/core/java/android/net/netlink/StructNlMsgHdr.java index 67925ac..98ab5e7 100644 --- a/core/java/android/net/netlink/StructNlMsgHdr.java +++ b/core/java/android/net/netlink/StructNlMsgHdr.java @@ -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 diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java index 0634281..a7bebad 100644 --- a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java +++ b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java @@ -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]); + } + } + } } |
