diff options
7 files changed, 148 insertions, 41 deletions
diff --git a/core/java/android/net/IpReachabilityMonitor.java b/core/java/android/net/IpReachabilityMonitor.java index cb8c866..b0f2003 100644 --- a/core/java/android/net/IpReachabilityMonitor.java +++ b/core/java/android/net/IpReachabilityMonitor.java @@ -76,6 +76,60 @@ public class IpReachabilityMonitor { private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; + /** + * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND) + * for the given IP address on the specified interface index. + * + * @return true, if the request was successfully passed to the kernel; false otherwise. + */ + public static boolean probeNeighbor(int ifIndex, InetAddress ip) { + final long IO_TIMEOUT = 300L; + // 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, ifIndex, null); + NetlinkSocket nlSocket = null; + boolean returnValue = false; + + try { + nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); + nlSocket.connectToKernel(); + nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); + final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); + final NetlinkMessage response = NetlinkMessage.parse(bytes); + if (response != null && response instanceof NetlinkErrorMessage && + (((NetlinkErrorMessage) response).getNlMsgError() != null) && + (((NetlinkErrorMessage) response).getNlMsgError().error == 0)) { + returnValue = true; + } else { + String errmsg; + if (bytes == null) { + errmsg = "null recvMessage"; + } else if (response == null) { + bytes.position(0); + errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); + } else { + errmsg = response.toString(); + } + Log.e(TAG, "Error probing ip=" + ip.getHostAddress() + + ", errmsg=" + errmsg); + } + } catch (ErrnoException | InterruptedIOException | SocketException e) { + Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e); + } + + if (nlSocket != null) { + nlSocket.close(); + } + return returnValue; + } + public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException { mInterfaceName = ifName; int ifIndex = -1; @@ -216,38 +270,10 @@ public class IpReachabilityMonitor { 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()); + if (!stillRunning()) { + break; } - } catch (ErrnoException | InterruptedIOException | SocketException e) { - Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e); - } - - if (nlSocket != null) { - nlSocket.close(); + probeNeighbor(mInterfaceIndex, target); } } diff --git a/core/java/android/net/netlink/NetlinkErrorMessage.java b/core/java/android/net/netlink/NetlinkErrorMessage.java index dbc10d6..e275574 100644 --- a/core/java/android/net/netlink/NetlinkErrorMessage.java +++ b/core/java/android/net/netlink/NetlinkErrorMessage.java @@ -18,7 +18,6 @@ package android.net.netlink; import android.net.netlink.StructNlMsgHdr; import android.net.netlink.NetlinkMessage; -import android.util.Log; import java.nio.ByteBuffer; diff --git a/core/java/android/net/netlink/NetlinkMessage.java b/core/java/android/net/netlink/NetlinkMessage.java index bc04a16..3bf75ca 100644 --- a/core/java/android/net/netlink/NetlinkMessage.java +++ b/core/java/android/net/netlink/NetlinkMessage.java @@ -60,7 +60,7 @@ public class NetlinkMessage { switch (nlmsghdr.nlmsg_type) { //case NetlinkConstants.NLMSG_NOOP: case NetlinkConstants.NLMSG_ERROR: - return (NetlinkMessage) NetlinkErrorMessage.parse(byteBuffer); + return (NetlinkMessage) NetlinkErrorMessage.parse(nlmsghdr, byteBuffer); case NetlinkConstants.NLMSG_DONE: byteBuffer.position(byteBuffer.position() + payloadLength); return new NetlinkMessage(nlmsghdr); diff --git a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java index b5f5817..02df131 100644 --- a/core/java/android/net/netlink/RtNetlinkNeighborMessage.java +++ b/core/java/android/net/netlink/RtNetlinkNeighborMessage.java @@ -16,6 +16,11 @@ package android.net.netlink; +import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK; +import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; +import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE; +import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; + import android.net.netlink.StructNdaCacheInfo; import android.net.netlink.StructNdMsg; import android.net.netlink.StructNlAttr; @@ -123,7 +128,7 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage { 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_flags = NLM_F_REQUEST | NLM_F_DUMP; nlmsghdr.nlmsg_seq = seqNo; nlmsghdr.pack(byteBuffer); @@ -141,7 +146,7 @@ public class RtNetlinkNeighborMessage extends NetlinkMessage { 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_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE; nlmsghdr.nlmsg_seq = seqNo; final RtNetlinkNeighborMessage msg = new RtNetlinkNeighborMessage(nlmsghdr); diff --git a/core/java/android/net/netlink/StructNlMsgErr.java b/core/java/android/net/netlink/StructNlMsgErr.java index 6b02650..f095af4 100644 --- a/core/java/android/net/netlink/StructNlMsgErr.java +++ b/core/java/android/net/netlink/StructNlMsgErr.java @@ -52,11 +52,6 @@ public class StructNlMsgErr { public int error; public StructNlMsgHdr msg; - public StructNlMsgErr() { - error = 0; - msg = null; - } - 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 diff --git a/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java b/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java new file mode 100644 index 0000000..e677475 --- /dev/null +++ b/core/tests/coretests/src/android/net/netlink/NetlinkErrorMessageTest.java @@ -0,0 +1,82 @@ +/* + * 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 static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; +import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK; +import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE; + +import android.net.netlink.NetlinkConstants; +import android.net.netlink.NetlinkErrorMessage; +import android.net.netlink.NetlinkMessage; +import android.net.netlink.StructNlMsgErr; +import android.util.Log; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import junit.framework.TestCase; +import libcore.util.HexEncoding; + + +public class NetlinkErrorMessageTest extends TestCase { + private final String TAG = "NetlinkErrorMessageTest"; + + // Hexadecimal representation of packet capture. + public static final String NLM_ERROR_OK_HEX = + // struct nlmsghdr + "24000000" + // length = 36 + "0200" + // type = 2 (NLMSG_ERROR) + "0000" + // flags + "26350000" + // seqno + "64100000" + // pid = userspace process + // error integer + "00000000" + // "errno" (0 == OK) + // struct nlmsghdr + "30000000" + // length (48) of original request + "1C00" + // type = 28 (RTM_NEWNEIGH) + "0501" + // flags (NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE) + "26350000" + // seqno + "00000000"; // pid = kernel + public static final byte[] NLM_ERROR_OK = + HexEncoding.decode(NLM_ERROR_OK_HEX.toCharArray(), false); + + public void testParseNlmErrorOk() { + final ByteBuffer byteBuffer = ByteBuffer.wrap(NLM_ERROR_OK); + byteBuffer.order(ByteOrder.LITTLE_ENDIAN); // For testing. + final NetlinkMessage msg = NetlinkMessage.parse(byteBuffer); + assertNotNull(msg); + assertTrue(msg instanceof NetlinkErrorMessage); + final NetlinkErrorMessage errorMsg = (NetlinkErrorMessage) msg; + + final StructNlMsgHdr hdr = errorMsg.getHeader(); + assertNotNull(hdr); + assertEquals(36, hdr.nlmsg_len); + assertEquals(NetlinkConstants.NLMSG_ERROR, hdr.nlmsg_type); + assertEquals(0, hdr.nlmsg_flags); + assertEquals(13606, hdr.nlmsg_seq); + assertEquals(4196, hdr.nlmsg_pid); + + final StructNlMsgErr err = errorMsg.getNlMsgError(); + assertNotNull(err); + assertEquals(0, err.error); + assertNotNull(err.msg); + assertEquals(48, err.msg.nlmsg_len); + assertEquals(NetlinkConstants.RTM_NEWNEIGH, err.msg.nlmsg_type); + assertEquals((NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE), err.msg.nlmsg_flags); + assertEquals(13606, err.msg.nlmsg_seq); + assertEquals(0, err.msg.nlmsg_pid); + } +} diff --git a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java index a7bebad..19ee000 100644 --- a/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java +++ b/core/tests/coretests/src/android/net/netlink/RtNetlinkNeighborMessageTest.java @@ -222,7 +222,7 @@ public class RtNetlinkNeighborMessageTest extends TestCase { // struct nlmsghdr "30000000" + // length = 48 "1c00" + // type = 28 (RTM_NEWNEIGH) - "0101" + // flags (NLM_F_REQUEST | NLM_F_REPLACE) + "0501" + // flags (NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE) "4b0a0000" + // seqno "00000000" + // pid (0 == kernel) // struct ndmsg |