Loading core/java/android/net/IpReachabilityMonitor.java +125 −87 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,11 @@ package android.net; package android.net; import com.android.internal.annotations.GuardedBy; import android.net.LinkAddress; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.ProxyInfo; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkConstants; Loading @@ -32,7 +35,6 @@ import android.os.SystemClock; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.NetlinkSocketAddress; import android.system.OsConstants; import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; import android.util.Log; import java.io.InterruptedIOException; import java.io.InterruptedIOException; Loading @@ -46,6 +48,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Set; Loading @@ -63,6 +66,10 @@ public class IpReachabilityMonitor { private static final boolean VDBG = false; private static final boolean VDBG = false; public interface Callback { public interface Callback { // This callback function must execute as quickly as possible as it is // run on the same thread that listens to kernel neighbor updates. // // TODO: refactor to something like notifyProvisioningLost(String msg). public void notifyLost(InetAddress ip, String logMsg); public void notifyLost(InetAddress ip, String logMsg); } } Loading @@ -70,11 +77,18 @@ public class IpReachabilityMonitor { private final String mInterfaceName; private final String mInterfaceName; private final int mInterfaceIndex; private final int mInterfaceIndex; private final Callback mCallback; private final Callback mCallback; private final Set<InetAddress> mIpWatchList; private int mIpWatchListVersion; private boolean mRunning; private final NetlinkSocketObserver mNetlinkSocketObserver; private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; private final Thread mObserverThread; @GuardedBy("mLock") private LinkProperties mLinkProperties = new LinkProperties(); // TODO: consider a map to a private NeighborState class holding more // information than a single NUD state entry. @GuardedBy("mLock") private Map<InetAddress, Short> mIpWatchList = new HashMap<>(); @GuardedBy("mLock") private int mIpWatchListVersion; @GuardedBy("mLock") private boolean mRunning; /** /** * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND) * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND) Loading @@ -84,21 +98,20 @@ public class IpReachabilityMonitor { */ */ public static boolean probeNeighbor(int ifIndex, InetAddress ip) { public static boolean probeNeighbor(int ifIndex, InetAddress ip) { final long IO_TIMEOUT = 300L; final long IO_TIMEOUT = 300L; final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; // This currently does not cause neighbor probing if the target |ip| // This currently does not cause neighbor probing if the target |ip| // has been confirmed reachable within the past "delay_probe_time" // has been confirmed reachable within the past "delay_probe_time" // seconds, i.e. within the past 5 seconds. // seconds, i.e. within the past 5 seconds. // // // TODO: replace with a transition directly to NUD_PROBE state once // TODO: replace with a transition directly to NUD_PROBE state once // kernels are updated to do so correctly. // kernels are updated to do so correctly. if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); } if (DBG) { Log.d(TAG, msgSnippet); } final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( 1, ip, StructNdMsg.NUD_DELAY, ifIndex, null); 1, ip, StructNdMsg.NUD_DELAY, ifIndex, null); NetlinkSocket nlSocket = null; boolean returnValue = false; boolean returnValue = false; try { try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) { nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); nlSocket.connectToKernel(); nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); Loading @@ -115,18 +128,17 @@ public class IpReachabilityMonitor { bytes.position(0); bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { } else { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. errmsg = response.toString(); errmsg = response.toString(); } } Log.e(TAG, "Error probing ip=" + ip.getHostAddress() + Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg); ", errmsg=" + errmsg); } } } catch (ErrnoException | InterruptedIOException | SocketException e) { } catch (ErrnoException | InterruptedIOException | SocketException e) { Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e); Log.d(TAG, "Error " + msgSnippet, e); } } if (nlSocket != null) { nlSocket.close(); } return returnValue; return returnValue; } } Loading @@ -140,9 +152,6 @@ public class IpReachabilityMonitor { throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); } } mCallback = callback; mCallback = callback; mIpWatchList = new HashSet<InetAddress>(); mIpWatchListVersion = 0; mRunning = false; mNetlinkSocketObserver = new NetlinkSocketObserver(); mNetlinkSocketObserver = new NetlinkSocketObserver(); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread.start(); mObserverThread.start(); Loading @@ -156,85 +165,85 @@ public class IpReachabilityMonitor { // TODO: add a public dump() method that can be called during a bug report. // TODO: add a public dump() method that can be called during a bug report. private static Set<InetAddress> getOnLinkNeighbors(LinkProperties lp) { private String describeWatchList() { Set<InetAddress> allIps = new HashSet<InetAddress>(); final String delimiter = ", "; StringBuilder sb = new StringBuilder(); final List<RouteInfo> routes = lp.getRoutes(); synchronized (mLock) { for (RouteInfo route : routes) { sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, "); if (route.hasGateway()) { sb.append("v{" + mIpWatchListVersion + "}, "); allIps.add(route.getGateway()); sb.append("ntable=["); boolean firstTime = true; for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { if (firstTime) { firstTime = false; } else { sb.append(delimiter); } } sb.append(entry.getKey().getHostAddress() + "/" + StructNdMsg.stringForNudState(entry.getValue())); } } sb.append("]"); for (InetAddress nameserver : lp.getDnsServers()) { allIps.add(nameserver); } } return sb.toString(); try { // Don't block here for DNS lookups. If the proxy happens to be an // IP literal then we add it the list, but otherwise skip it. allIps.add(NetworkUtils.numericToInetAddress(lp.getHttpProxy().getHost())); } catch (NullPointerException|IllegalArgumentException e) { // No proxy, PAC proxy, or proxy is not a literal IP address. } } Set<InetAddress> neighbors = new HashSet<InetAddress>(); private boolean isWatching(InetAddress ip) { for (InetAddress ip : allIps) { synchronized (mLock) { // TODO: consider using the prefixes of the LinkAddresses instead return mRunning && mIpWatchList.containsKey(ip); // of the routes--it may be more accurate. for (RouteInfo route : routes) { if (route.hasGateway()) { continue; // Not directly connected. } if (route.matches(ip)) { neighbors.add(ip); break; } } } } return neighbors; } } private String describeWatchList() { private boolean stillRunning() { synchronized (mLock) { synchronized (mLock) { return "version{" + mIpWatchListVersion + "}, " + return mRunning; "ips=[" + TextUtils.join(",", mIpWatchList) + "]"; } } } } private boolean isWatching(InetAddress ip) { private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { synchronized (mLock) { for (RouteInfo route : routes) { return mRunning && mIpWatchList.contains(ip); if (!route.hasGateway() && route.matches(ip)) { return true; } } } return false; } } private boolean stillRunning() { private short getNeighborStateLocked(InetAddress ip) { synchronized (mLock) { if (mIpWatchList.containsKey(ip)) { return mRunning; return mIpWatchList.get(ip); } } return StructNdMsg.NUD_NONE; } } public void updateLinkProperties(LinkProperties lp) { public void updateLinkProperties(LinkProperties lp) { if (!mInterfaceName.equals(lp.getInterfaceName())) { if (!mInterfaceName.equals(lp.getInterfaceName())) { // TODO: figure out how to cope with interface changes. // TODO: figure out whether / how to cope with interface changes. Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + "' does not match: " + mInterfaceName); "' does not match: " + mInterfaceName); return; return; } } // We rely upon the caller to determine when LinkProperties have actually synchronized (mLock) { // changed and call this at the appropriate time. Note that even though mLinkProperties = new LinkProperties(lp); // the LinkProperties may change, the set of on-link neighbors might not. Map<InetAddress, Short> newIpWatchList = new HashMap<>(); // // Nevertheless, just clear and re-add everything. final List<RouteInfo> routes = mLinkProperties.getRoutes(); final Set<InetAddress> neighbors = getOnLinkNeighbors(lp); for (RouteInfo route : routes) { if (neighbors.isEmpty()) { if (route.hasGateway()) { return; InetAddress gw = route.getGateway(); if (isOnLink(routes, gw)) { newIpWatchList.put(gw, getNeighborStateLocked(gw)); } } } } synchronized (mLock) { for (InetAddress nameserver : lp.getDnsServers()) { mIpWatchList.clear(); if (isOnLink(routes, nameserver)) { mIpWatchList.addAll(neighbors); newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver)); } } mIpWatchList = newIpWatchList; mIpWatchListVersion++; mIpWatchListVersion++; } } if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } Loading @@ -242,32 +251,51 @@ public class IpReachabilityMonitor { public void clearLinkProperties() { public void clearLinkProperties() { synchronized (mLock) { synchronized (mLock) { mLinkProperties.clear(); mIpWatchList.clear(); mIpWatchList.clear(); mIpWatchListVersion++; mIpWatchListVersion++; } } if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } } } private void notifyLost(InetAddress ip, String msg) { private void handleNeighborLost(String msg) { if (!isWatching(ip)) { InetAddress ip = null; // Ignore stray notifications. This can happen when, for example, ProvisioningChange delta; // several neighbors are reported unreachable or deleted synchronized (mLock) { // back-to-back. Because these messages are parsed serially, and LinkProperties whatIfLp = new LinkProperties(mLinkProperties); // this method is called for each notification, the caller above us // may have already processed an earlier lost notification and for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { // cleared the watch list as it moves to handle the situation. if (entry.getValue() != StructNdMsg.NUD_FAILED) { return; continue; } ip = entry.getKey(); for (RouteInfo route : mLinkProperties.getRoutes()) { if (ip.equals(route.getGateway())) { whatIfLp.removeRoute(route); } } whatIfLp.removeDnsServer(ip); } } Log.w(TAG, "ALERT: " + ip.getHostAddress() + " -- " + msg); delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp); } if (delta == ProvisioningChange.LOST_PROVISIONING) { final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; Log.w(TAG, logMsg); if (mCallback != null) { if (mCallback != null) { mCallback.notifyLost(ip, msg); // TODO: remove |ip| when the callback signature no longer has // an InetAddress argument. mCallback.notifyLost(ip, logMsg); } } } } } public void probeAll() { public void probeAll() { Set<InetAddress> ipProbeList = new HashSet<InetAddress>(); Set<InetAddress> ipProbeList = new HashSet<InetAddress>(); synchronized (mLock) { synchronized (mLock) { ipProbeList.addAll(mIpWatchList); ipProbeList.addAll(mIpWatchList.keySet()); } } for (InetAddress target : ipProbeList) { for (InetAddress target : ipProbeList) { if (!stillRunning()) { if (!stillRunning()) { Loading Loading @@ -411,10 +439,20 @@ public class IpReachabilityMonitor { Log.d(TAG, eventMsg); Log.d(TAG, eventMsg); } } synchronized (mLock) { if (mIpWatchList.containsKey(destination)) { final short value = (msgType == NetlinkConstants.RTM_DELNEIGH) ? StructNdMsg.NUD_FAILED : nudState; mIpWatchList.put(destination, value); } } if ((msgType == NetlinkConstants.RTM_DELNEIGH) || if ((msgType == NetlinkConstants.RTM_DELNEIGH) || (nudState == StructNdMsg.NUD_FAILED)) { (nudState == StructNdMsg.NUD_FAILED)) { final String logMsg = "FAILURE: " + eventMsg; Log.w(TAG, "ALERT: " + eventMsg); notifyLost(destination, logMsg); handleNeighborLost(eventMsg); } } } } } } Loading core/java/android/net/netlink/StructNdMsg.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -33,6 +33,7 @@ public class StructNdMsg { public static final int STRUCT_SIZE = 12; public static final int STRUCT_SIZE = 12; // Neighbor Cache Entry States // Neighbor Cache Entry States public static final short NUD_NONE = 0x00; public static final short NUD_INCOMPLETE = 0x01; public static final short NUD_INCOMPLETE = 0x01; public static final short NUD_REACHABLE = 0x02; public static final short NUD_REACHABLE = 0x02; public static final short NUD_STALE = 0x04; public static final short NUD_STALE = 0x04; Loading @@ -44,6 +45,7 @@ public class StructNdMsg { public static String stringForNudState(short nudState) { public static String stringForNudState(short nudState) { switch (nudState) { switch (nudState) { case NUD_NONE: return "NUD_NONE"; case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; case NUD_REACHABLE: return "NUD_REACHABLE"; case NUD_REACHABLE: return "NUD_REACHABLE"; case NUD_STALE: return "NUD_STALE"; case NUD_STALE: return "NUD_STALE"; Loading Loading
core/java/android/net/IpReachabilityMonitor.java +125 −87 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,11 @@ package android.net; package android.net; import com.android.internal.annotations.GuardedBy; import android.net.LinkAddress; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.LinkProperties; import android.net.LinkProperties.ProvisioningChange; import android.net.ProxyInfo; import android.net.ProxyInfo; import android.net.RouteInfo; import android.net.RouteInfo; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkConstants; Loading @@ -32,7 +35,6 @@ import android.os.SystemClock; import android.system.ErrnoException; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; import android.system.NetlinkSocketAddress; import android.system.OsConstants; import android.system.OsConstants; import android.text.TextUtils; import android.util.Log; import android.util.Log; import java.io.InterruptedIOException; import java.io.InterruptedIOException; Loading @@ -46,6 +48,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashMap; import java.util.HashSet; import java.util.HashSet; import java.util.List; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Set; Loading @@ -63,6 +66,10 @@ public class IpReachabilityMonitor { private static final boolean VDBG = false; private static final boolean VDBG = false; public interface Callback { public interface Callback { // This callback function must execute as quickly as possible as it is // run on the same thread that listens to kernel neighbor updates. // // TODO: refactor to something like notifyProvisioningLost(String msg). public void notifyLost(InetAddress ip, String logMsg); public void notifyLost(InetAddress ip, String logMsg); } } Loading @@ -70,11 +77,18 @@ public class IpReachabilityMonitor { private final String mInterfaceName; private final String mInterfaceName; private final int mInterfaceIndex; private final int mInterfaceIndex; private final Callback mCallback; private final Callback mCallback; private final Set<InetAddress> mIpWatchList; private int mIpWatchListVersion; private boolean mRunning; private final NetlinkSocketObserver mNetlinkSocketObserver; private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; private final Thread mObserverThread; @GuardedBy("mLock") private LinkProperties mLinkProperties = new LinkProperties(); // TODO: consider a map to a private NeighborState class holding more // information than a single NUD state entry. @GuardedBy("mLock") private Map<InetAddress, Short> mIpWatchList = new HashMap<>(); @GuardedBy("mLock") private int mIpWatchListVersion; @GuardedBy("mLock") private boolean mRunning; /** /** * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND) * Make the kernel to perform neighbor reachability detection (IPv4 ARP or IPv6 ND) Loading @@ -84,21 +98,20 @@ public class IpReachabilityMonitor { */ */ public static boolean probeNeighbor(int ifIndex, InetAddress ip) { public static boolean probeNeighbor(int ifIndex, InetAddress ip) { final long IO_TIMEOUT = 300L; final long IO_TIMEOUT = 300L; final String msgSnippet = "probing ip=" + ip.getHostAddress() + "%" + ifIndex; // This currently does not cause neighbor probing if the target |ip| // This currently does not cause neighbor probing if the target |ip| // has been confirmed reachable within the past "delay_probe_time" // has been confirmed reachable within the past "delay_probe_time" // seconds, i.e. within the past 5 seconds. // seconds, i.e. within the past 5 seconds. // // // TODO: replace with a transition directly to NUD_PROBE state once // TODO: replace with a transition directly to NUD_PROBE state once // kernels are updated to do so correctly. // kernels are updated to do so correctly. if (DBG) { Log.d(TAG, "Probing ip=" + ip.getHostAddress()); } if (DBG) { Log.d(TAG, msgSnippet); } final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage( 1, ip, StructNdMsg.NUD_DELAY, ifIndex, null); 1, ip, StructNdMsg.NUD_DELAY, ifIndex, null); NetlinkSocket nlSocket = null; boolean returnValue = false; boolean returnValue = false; try { try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) { nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); nlSocket.connectToKernel(); nlSocket.connectToKernel(); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT); Loading @@ -115,18 +128,17 @@ public class IpReachabilityMonitor { bytes.position(0); bytes.position(0); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes); } else { } else { // TODO: consider ignoring EINVAL (-22), which appears to be // normal when probing a neighbor for which the kernel does // not already have / no longer has a link layer address. errmsg = response.toString(); errmsg = response.toString(); } } Log.e(TAG, "Error probing ip=" + ip.getHostAddress() + Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg); ", errmsg=" + errmsg); } } } catch (ErrnoException | InterruptedIOException | SocketException e) { } catch (ErrnoException | InterruptedIOException | SocketException e) { Log.d(TAG, "Error probing ip=" + ip.getHostAddress(), e); Log.d(TAG, "Error " + msgSnippet, e); } } if (nlSocket != null) { nlSocket.close(); } return returnValue; return returnValue; } } Loading @@ -140,9 +152,6 @@ public class IpReachabilityMonitor { throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); } } mCallback = callback; mCallback = callback; mIpWatchList = new HashSet<InetAddress>(); mIpWatchListVersion = 0; mRunning = false; mNetlinkSocketObserver = new NetlinkSocketObserver(); mNetlinkSocketObserver = new NetlinkSocketObserver(); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread.start(); mObserverThread.start(); Loading @@ -156,85 +165,85 @@ public class IpReachabilityMonitor { // TODO: add a public dump() method that can be called during a bug report. // TODO: add a public dump() method that can be called during a bug report. private static Set<InetAddress> getOnLinkNeighbors(LinkProperties lp) { private String describeWatchList() { Set<InetAddress> allIps = new HashSet<InetAddress>(); final String delimiter = ", "; StringBuilder sb = new StringBuilder(); final List<RouteInfo> routes = lp.getRoutes(); synchronized (mLock) { for (RouteInfo route : routes) { sb.append("iface{" + mInterfaceName + "/" + mInterfaceIndex + "}, "); if (route.hasGateway()) { sb.append("v{" + mIpWatchListVersion + "}, "); allIps.add(route.getGateway()); sb.append("ntable=["); boolean firstTime = true; for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { if (firstTime) { firstTime = false; } else { sb.append(delimiter); } } sb.append(entry.getKey().getHostAddress() + "/" + StructNdMsg.stringForNudState(entry.getValue())); } } sb.append("]"); for (InetAddress nameserver : lp.getDnsServers()) { allIps.add(nameserver); } } return sb.toString(); try { // Don't block here for DNS lookups. If the proxy happens to be an // IP literal then we add it the list, but otherwise skip it. allIps.add(NetworkUtils.numericToInetAddress(lp.getHttpProxy().getHost())); } catch (NullPointerException|IllegalArgumentException e) { // No proxy, PAC proxy, or proxy is not a literal IP address. } } Set<InetAddress> neighbors = new HashSet<InetAddress>(); private boolean isWatching(InetAddress ip) { for (InetAddress ip : allIps) { synchronized (mLock) { // TODO: consider using the prefixes of the LinkAddresses instead return mRunning && mIpWatchList.containsKey(ip); // of the routes--it may be more accurate. for (RouteInfo route : routes) { if (route.hasGateway()) { continue; // Not directly connected. } if (route.matches(ip)) { neighbors.add(ip); break; } } } } return neighbors; } } private String describeWatchList() { private boolean stillRunning() { synchronized (mLock) { synchronized (mLock) { return "version{" + mIpWatchListVersion + "}, " + return mRunning; "ips=[" + TextUtils.join(",", mIpWatchList) + "]"; } } } } private boolean isWatching(InetAddress ip) { private static boolean isOnLink(List<RouteInfo> routes, InetAddress ip) { synchronized (mLock) { for (RouteInfo route : routes) { return mRunning && mIpWatchList.contains(ip); if (!route.hasGateway() && route.matches(ip)) { return true; } } } return false; } } private boolean stillRunning() { private short getNeighborStateLocked(InetAddress ip) { synchronized (mLock) { if (mIpWatchList.containsKey(ip)) { return mRunning; return mIpWatchList.get(ip); } } return StructNdMsg.NUD_NONE; } } public void updateLinkProperties(LinkProperties lp) { public void updateLinkProperties(LinkProperties lp) { if (!mInterfaceName.equals(lp.getInterfaceName())) { if (!mInterfaceName.equals(lp.getInterfaceName())) { // TODO: figure out how to cope with interface changes. // TODO: figure out whether / how to cope with interface changes. Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + "' does not match: " + mInterfaceName); "' does not match: " + mInterfaceName); return; return; } } // We rely upon the caller to determine when LinkProperties have actually synchronized (mLock) { // changed and call this at the appropriate time. Note that even though mLinkProperties = new LinkProperties(lp); // the LinkProperties may change, the set of on-link neighbors might not. Map<InetAddress, Short> newIpWatchList = new HashMap<>(); // // Nevertheless, just clear and re-add everything. final List<RouteInfo> routes = mLinkProperties.getRoutes(); final Set<InetAddress> neighbors = getOnLinkNeighbors(lp); for (RouteInfo route : routes) { if (neighbors.isEmpty()) { if (route.hasGateway()) { return; InetAddress gw = route.getGateway(); if (isOnLink(routes, gw)) { newIpWatchList.put(gw, getNeighborStateLocked(gw)); } } } } synchronized (mLock) { for (InetAddress nameserver : lp.getDnsServers()) { mIpWatchList.clear(); if (isOnLink(routes, nameserver)) { mIpWatchList.addAll(neighbors); newIpWatchList.put(nameserver, getNeighborStateLocked(nameserver)); } } mIpWatchList = newIpWatchList; mIpWatchListVersion++; mIpWatchListVersion++; } } if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } Loading @@ -242,32 +251,51 @@ public class IpReachabilityMonitor { public void clearLinkProperties() { public void clearLinkProperties() { synchronized (mLock) { synchronized (mLock) { mLinkProperties.clear(); mIpWatchList.clear(); mIpWatchList.clear(); mIpWatchListVersion++; mIpWatchListVersion++; } } if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } } } private void notifyLost(InetAddress ip, String msg) { private void handleNeighborLost(String msg) { if (!isWatching(ip)) { InetAddress ip = null; // Ignore stray notifications. This can happen when, for example, ProvisioningChange delta; // several neighbors are reported unreachable or deleted synchronized (mLock) { // back-to-back. Because these messages are parsed serially, and LinkProperties whatIfLp = new LinkProperties(mLinkProperties); // this method is called for each notification, the caller above us // may have already processed an earlier lost notification and for (Map.Entry<InetAddress, Short> entry : mIpWatchList.entrySet()) { // cleared the watch list as it moves to handle the situation. if (entry.getValue() != StructNdMsg.NUD_FAILED) { return; continue; } ip = entry.getKey(); for (RouteInfo route : mLinkProperties.getRoutes()) { if (ip.equals(route.getGateway())) { whatIfLp.removeRoute(route); } } whatIfLp.removeDnsServer(ip); } } Log.w(TAG, "ALERT: " + ip.getHostAddress() + " -- " + msg); delta = LinkProperties.compareProvisioning(mLinkProperties, whatIfLp); } if (delta == ProvisioningChange.LOST_PROVISIONING) { final String logMsg = "FAILURE: LOST_PROVISIONING, " + msg; Log.w(TAG, logMsg); if (mCallback != null) { if (mCallback != null) { mCallback.notifyLost(ip, msg); // TODO: remove |ip| when the callback signature no longer has // an InetAddress argument. mCallback.notifyLost(ip, logMsg); } } } } } public void probeAll() { public void probeAll() { Set<InetAddress> ipProbeList = new HashSet<InetAddress>(); Set<InetAddress> ipProbeList = new HashSet<InetAddress>(); synchronized (mLock) { synchronized (mLock) { ipProbeList.addAll(mIpWatchList); ipProbeList.addAll(mIpWatchList.keySet()); } } for (InetAddress target : ipProbeList) { for (InetAddress target : ipProbeList) { if (!stillRunning()) { if (!stillRunning()) { Loading Loading @@ -411,10 +439,20 @@ public class IpReachabilityMonitor { Log.d(TAG, eventMsg); Log.d(TAG, eventMsg); } } synchronized (mLock) { if (mIpWatchList.containsKey(destination)) { final short value = (msgType == NetlinkConstants.RTM_DELNEIGH) ? StructNdMsg.NUD_FAILED : nudState; mIpWatchList.put(destination, value); } } if ((msgType == NetlinkConstants.RTM_DELNEIGH) || if ((msgType == NetlinkConstants.RTM_DELNEIGH) || (nudState == StructNdMsg.NUD_FAILED)) { (nudState == StructNdMsg.NUD_FAILED)) { final String logMsg = "FAILURE: " + eventMsg; Log.w(TAG, "ALERT: " + eventMsg); notifyLost(destination, logMsg); handleNeighborLost(eventMsg); } } } } } } Loading
core/java/android/net/netlink/StructNdMsg.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -33,6 +33,7 @@ public class StructNdMsg { public static final int STRUCT_SIZE = 12; public static final int STRUCT_SIZE = 12; // Neighbor Cache Entry States // Neighbor Cache Entry States public static final short NUD_NONE = 0x00; public static final short NUD_INCOMPLETE = 0x01; public static final short NUD_INCOMPLETE = 0x01; public static final short NUD_REACHABLE = 0x02; public static final short NUD_REACHABLE = 0x02; public static final short NUD_STALE = 0x04; public static final short NUD_STALE = 0x04; Loading @@ -44,6 +45,7 @@ public class StructNdMsg { public static String stringForNudState(short nudState) { public static String stringForNudState(short nudState) { switch (nudState) { switch (nudState) { case NUD_NONE: return "NUD_NONE"; case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; case NUD_INCOMPLETE: return "NUD_INCOMPLETE"; case NUD_REACHABLE: return "NUD_REACHABLE"; case NUD_REACHABLE: return "NUD_REACHABLE"; case NUD_STALE: return "NUD_STALE"; case NUD_STALE: return "NUD_STALE"; Loading