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