IpReachabilityMonitor.java revision 787d935611c8a6d1ed66e4d718f78084fe7bd3c5
1787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline/* 2787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * Copyright (C) 2015 The Android Open Source Project 3787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * 4787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * Licensed under the Apache License, Version 2.0 (the "License"); 5787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * you may not use this file except in compliance with the License. 6787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * You may obtain a copy of the License at 7787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * 8787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * http://www.apache.org/licenses/LICENSE-2.0 9787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * 10787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * Unless required by applicable law or agreed to in writing, software 11787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * distributed under the License is distributed on an "AS IS" BASIS, 12787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * See the License for the specific language governing permissions and 14787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * limitations under the License. 15787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline */ 16787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 17787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klinepackage android.net; 18787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 19787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.LinkAddress; 20787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.LinkProperties; 21787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.ProxyInfo; 22787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.RouteInfo; 23787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.NetlinkConstants; 24787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.NetlinkErrorMessage; 25787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.NetlinkMessage; 26787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.NetlinkSocket; 27787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.RtNetlinkNeighborMessage; 28787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.StructNdaCacheInfo; 29787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.StructNdMsg; 30787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.net.netlink.StructNlMsgHdr; 31787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.os.SystemClock; 32787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.system.ErrnoException; 33787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.system.NetlinkSocketAddress; 34787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.system.OsConstants; 35787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.text.TextUtils; 36787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport android.util.Log; 37787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 38787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.io.InterruptedIOException; 39787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.net.InetAddress; 40787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.net.InetSocketAddress; 41787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.net.NetworkInterface; 42787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.net.SocketAddress; 43787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.net.SocketException; 44787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.nio.ByteBuffer; 45787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.util.Arrays; 46787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.util.HashMap; 47787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.util.HashSet; 48787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.util.List; 49787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klineimport java.util.Set; 50787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 51787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 52787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline/** 53787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * IpReachabilityMonitor. 54787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * 55787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * Monitors on-link IP reachability and notifies callers whenever any on-link 56787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * addresses of interest appear to have become unresponsive. 57787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * 58787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline * @hide 59787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline */ 60787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Klinepublic class IpReachabilityMonitor { 61787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private static final String TAG = "IpReachabilityMonitor"; 62787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private static final boolean DBG = true; 63787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private static final boolean VDBG = false; 64787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 65787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public interface Callback { 66787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public void notifyLost(InetAddress ip, String logMsg); 67787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 68787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 69787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final Object mLock = new Object(); 70787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final String mInterfaceName; 71787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final int mInterfaceIndex; 72787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final Callback mCallback; 73787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final Set<InetAddress> mIpWatchList; 74787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private int mIpWatchListVersion; 75787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private boolean mRunning; 76787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final private Thread mObserverThread; 77787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 78787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // TODO: consider passing in a NetworkInterface object from the caller. 79787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public IpReachabilityMonitor(String ifName, Callback callback) throws IllegalArgumentException { 80787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mInterfaceName = ifName; 81787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline int ifIndex = -1; 82787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline try { 83787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline NetworkInterface netIf = NetworkInterface.getByName(ifName); 84787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mInterfaceIndex = netIf.getIndex(); 85787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (SocketException | NullPointerException e) { 86787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline throw new IllegalArgumentException("invalid interface '" + ifName + "': ", e); 87787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 88787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mCallback = callback; 89787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchList = new HashSet<InetAddress>(); 90787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchListVersion = 0; 91787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mRunning = false; 92787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mObserverThread = new Thread(new NetlinkSocketObserver()); 93787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mObserverThread.start(); 94787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 95787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 96787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public void stop() { 97787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 98787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mRunning = false; 99787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchList.clear(); 100787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 101787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 102787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 103787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // TODO: add a public dump() method that can be called during a bug report. 104787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 105787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private static Set<InetAddress> getOnLinkNeighbors(LinkProperties lp) { 106787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Set<InetAddress> allIps = new HashSet<InetAddress>(); 107787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 108787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final List<RouteInfo> routes = lp.getRoutes(); 109787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline for (RouteInfo route : routes) { 110787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (route.hasGateway()) { 111787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline allIps.add(route.getGateway()); 112787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 113787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 114787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 115787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline for (InetAddress nameserver : lp.getDnsServers()) { 116787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline allIps.add(nameserver); 117787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 118787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 119787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline try { 120787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // Don't block here for DNS lookups. If the proxy happens to be an 121787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // IP literal then we add it the list, but otherwise skip it. 122787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline allIps.add(NetworkUtils.numericToInetAddress(lp.getHttpProxy().getHost())); 123787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (NullPointerException|IllegalArgumentException e) { 124787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // No proxy, PAC proxy, or proxy is not a literal IP address. 125787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 126787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 127787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Set<InetAddress> neighbors = new HashSet<InetAddress>(); 128787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline for (InetAddress ip : allIps) { 129787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // TODO: consider using the prefixes of the LinkAddresses instead 130787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // of the routes--it may be more accurate. 131787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline for (RouteInfo route : routes) { 132787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (route.hasGateway()) { 133787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline continue; // Not directly connected. 134787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 135787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (route.matches(ip)) { 136787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline neighbors.add(ip); 137787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline break; 138787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 139787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 140787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 141787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return neighbors; 142787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 143787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 144787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private String describeWatchList() { 145787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 146787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return "version{" + mIpWatchListVersion + "}, " + 147787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline "ips=[" + TextUtils.join(",", mIpWatchList) + "]"; 148787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 149787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 150787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 151787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private boolean isWatching(InetAddress ip) { 152787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 153787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return mRunning && mIpWatchList.contains(ip); 154787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 155787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 156787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 157787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public void updateLinkProperties(LinkProperties lp) { 158787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (!mInterfaceName.equals(lp.getInterfaceName())) { 159787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // TODO: figure out how to cope with interface changes. 160787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.wtf(TAG, "requested LinkProperties interface '" + lp.getInterfaceName() + 161787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline "' does not match: " + mInterfaceName); 162787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return; 163787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 164787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 165787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // We rely upon the caller to determine when LinkProperties have actually 166787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // changed and call this at the appropriate time. Note that even though 167787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // the LinkProperties may change, the set of on-link neighbors might not. 168787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // 169787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // Nevertheless, just clear and re-add everything. 170787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final Set<InetAddress> neighbors = getOnLinkNeighbors(lp); 171787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (neighbors.isEmpty()) { 172787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return; 173787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 174787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 175787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 176787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchList.clear(); 177787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchList.addAll(neighbors); 178787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchListVersion++; 179787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 180787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (DBG) { Log.d(TAG, "watch: " + describeWatchList()); } 181787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 182787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 183787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public void clearLinkProperties() { 184787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 185787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchList.clear(); 186787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mIpWatchListVersion++; 187787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 188787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (DBG) { Log.d(TAG, "clear: " + describeWatchList()); } 189787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 190787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 191787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private void notifyLost(InetAddress ip, String msg) { 192787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (!isWatching(ip)) { 193787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // Ignore stray notifications. This can happen when, for example, 194787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // several neighbors are reported unreachable or deleted 195787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // back-to-back. Because these messages are parsed serially, and 196787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // this method is called for each notification, the caller above us 197787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // may have already processed an earlier lost notification and 198787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // cleared the watch list as it moves to handle the situation. 199787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return; 200787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 201787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.w(TAG, "ALERT: " + ip.getHostAddress() + " -- " + msg); 202787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (mCallback != null) { 203787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mCallback.notifyLost(ip, msg); 204787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 205787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 206787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 207787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 208787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private final class NetlinkSocketObserver implements Runnable { 209787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private static final String TAG = "NetlinkSocketObserver"; 210787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private NetlinkSocket mSocket; 211787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 212787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline @Override 213787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline public void run() { 214787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (VDBG) { Log.d(TAG, "Starting observing thread."); } 215787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { mRunning = true; } 216787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 217787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline try { 218787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline setupNetlinkSocket(); 219787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (ErrnoException | SocketException e) { 220787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.e(TAG, "Failed to suitably initialize a netlink socket", e); 221787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { mRunning = false; } 222787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 223787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 224787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline ByteBuffer byteBuffer; 225787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline while (stillRunning()) { 226787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline try { 227787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline byteBuffer = recvKernelReply(); 228787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (ErrnoException e) { 229787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.w(TAG, "ErrnoException: ", e); 230787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline break; 231787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 232787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final long whenMs = SystemClock.elapsedRealtime(); 233787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (byteBuffer == null) { 234787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline continue; 235787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 236787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline parseNetlinkMessageBuffer(byteBuffer, whenMs); 237787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 238787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 239787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline clearNetlinkSocket(); 240787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 241787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { mRunning = false; } 242787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (VDBG) { Log.d(TAG, "Finishing observing thread."); } 243787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 244787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 245787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private boolean stillRunning() { 246787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline synchronized (mLock) { 247787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return mRunning; 248787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 249787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 250787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 251787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private void clearNetlinkSocket() { 252787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (mSocket != null) { 253787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mSocket.close(); 254787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 255787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mSocket = null; 256787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 257787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 258787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // TODO: Refactor the main loop to recreate the socket upon recoverable errors. 259787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private void setupNetlinkSocket() throws ErrnoException, SocketException { 260787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline clearNetlinkSocket(); 261787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE); 262787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 263787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final NetlinkSocketAddress listenAddr = new NetlinkSocketAddress( 264787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 0, OsConstants.RTMGRP_NEIGH); 265787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline mSocket.bind(listenAddr); 266787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 267787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (VDBG) { 268787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final NetlinkSocketAddress nlAddr = mSocket.getLocalAddress(); 269787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.d(TAG, "bound to sockaddr_nl{" 270787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + ((long) (nlAddr.getPortId() & 0xffffffff)) + ", " 271787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + nlAddr.getGroupsMask() 272787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + "}"); 273787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 274787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 275787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 276787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private ByteBuffer recvKernelReply() throws ErrnoException { 277787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline try { 278787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return mSocket.recvMessage(0); 279787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (InterruptedIOException e) { 280787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline // Interruption or other error, e.g. another thread closed our file descriptor. 281787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } catch (ErrnoException e) { 282787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (e.errno != OsConstants.EAGAIN) { 283787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline throw e; 284787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 285787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 286787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return null; 287787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 288787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 289787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private void parseNetlinkMessageBuffer(ByteBuffer byteBuffer, long whenMs) { 290787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline while (byteBuffer.remaining() > 0) { 291787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final int position = byteBuffer.position(); 292787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final NetlinkMessage nlMsg = NetlinkMessage.parse(byteBuffer); 293787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (nlMsg == null || nlMsg.getHeader() == null) { 294787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline byteBuffer.position(position); 295787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.e(TAG, "unparsable netlink msg: " + NetlinkConstants.hexify(byteBuffer)); 296787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline break; 297787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 298787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 299787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final int srcPortId = nlMsg.getHeader().nlmsg_pid; 300787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (srcPortId != 0) { 301787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.e(TAG, "non-kernel source portId: " + ((long) (srcPortId & 0xffffffff))); 302787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline break; 303787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 304787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 305787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (nlMsg instanceof NetlinkErrorMessage) { 306787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.e(TAG, "netlink error: " + nlMsg); 307787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline continue; 308787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } else if (!(nlMsg instanceof RtNetlinkNeighborMessage)) { 309787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (DBG) { 310787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.d(TAG, "non-rtnetlink neighbor msg: " + nlMsg); 311787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 312787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline continue; 313787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 314787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 315787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline evaluateRtNetlinkNeighborMessage((RtNetlinkNeighborMessage) nlMsg, whenMs); 316787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 317787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 318787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 319787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline private void evaluateRtNetlinkNeighborMessage( 320787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline RtNetlinkNeighborMessage neighMsg, long whenMs) { 321787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final StructNdMsg ndMsg = neighMsg.getNdHeader(); 322787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (ndMsg == null || ndMsg.ndm_ifindex != mInterfaceIndex) { 323787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return; 324787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 325787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 326787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final InetAddress destination = neighMsg.getDestination(); 327787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (!isWatching(destination)) { 328787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline return; 329787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 330787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 331787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final short msgType = neighMsg.getHeader().nlmsg_type; 332787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final short nudState = ndMsg.ndm_state; 333787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final String eventMsg = "NeighborEvent{" 334787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + "elapsedMs=" + whenMs + ", " 335787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + destination.getHostAddress() + ", " 336787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + "[" + NetlinkConstants.hexify(neighMsg.getLinkLayerAddress()) + "], " 337787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + NetlinkConstants.stringForNlMsgType(msgType) + ", " 338787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + StructNdMsg.stringForNudState(nudState) 339787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline + "}"; 340787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 341787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if (VDBG) { 342787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.d(TAG, neighMsg.toString()); 343787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } else if (DBG) { 344787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline Log.d(TAG, eventMsg); 345787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 346787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline 347787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline if ((msgType == NetlinkConstants.RTM_DELNEIGH) || 348787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline (nudState == StructNdMsg.NUD_FAILED)) { 349787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline final String logMsg = "FAILURE: " + eventMsg; 350787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline notifyLost(destination, logMsg); 351787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 352787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 353787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline } 354787d935611c8a6d1ed66e4d718f78084fe7bd3c5Erik Kline} 355