1379747a4040118130ca35947aef6942b2cd1cd67Erik Kline/*
2379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Copyright (C) 2015 The Android Open Source Project
3379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
4379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Licensed under the Apache License, Version 2.0 (the "License");
5379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * you may not use this file except in compliance with the License.
6379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * You may obtain a copy of the License at
7379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
8379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *      http://www.apache.org/licenses/LICENSE-2.0
9379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
10379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Unless required by applicable law or agreed to in writing, software
11379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * distributed under the License is distributed on an "AS IS" BASIS,
12379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * See the License for the specific language governing permissions and
14379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * limitations under the License.
15379747a4040118130ca35947aef6942b2cd1cd67Erik Kline */
16379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
17379747a4040118130ca35947aef6942b2cd1cd67Erik Klinepackage com.android.server.connectivity;
18379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
19379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport static android.system.OsConstants.*;
20379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
2129f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colittiimport android.net.LinkAddress;
22379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.LinkProperties;
23379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.Network;
2487cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colittiimport android.net.NetworkUtils;
25379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.net.RouteInfo;
26619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkeyimport android.net.TrafficStats;
27918a68b3ca3d375335691230cc8fd7c974403717Chalard Jeanimport android.net.util.NetworkConstants;
28379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.os.SystemClock;
29379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.ErrnoException;
30379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.Os;
31379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.system.StructTimeval;
32379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport android.text.TextUtils;
3329f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colittiimport android.util.Pair;
34379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
35379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport com.android.internal.util.IndentingPrintWriter;
36379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
37379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.Closeable;
38379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.FileDescriptor;
39379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.InterruptedIOException;
40379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.io.IOException;
41379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.Inet4Address;
42379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.Inet6Address;
43379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.InetAddress;
44379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.InetSocketAddress;
45379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.NetworkInterface;
46379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.SocketAddress;
47379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.SocketException;
48379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.net.UnknownHostException;
49379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.nio.ByteBuffer;
50379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.nio.charset.StandardCharsets;
51379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.concurrent.CountDownLatch;
52379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.concurrent.TimeUnit;
53379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Arrays;
54106cdf6c94c455462316555b7ac59ec79c3b40faErik Klineimport java.util.ArrayList;
55379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.HashMap;
56106cdf6c94c455462316555b7ac59ec79c3b40faErik Klineimport java.util.List;
57379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Map;
58379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport java.util.Random;
59379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
60379747a4040118130ca35947aef6942b2cd1cd67Erik Klineimport libcore.io.IoUtils;
61379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
62379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
63379747a4040118130ca35947aef6942b2cd1cd67Erik Kline/**
64379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * NetworkDiagnostics
65379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
66379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * A simple class to diagnose network connectivity fundamentals.  Current
67379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * checks performed are:
68379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - ICMPv4/v6 echo requests for all routers
69379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - ICMPv4/v6 echo requests for all DNS servers
70379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - DNS UDP queries to all DNS servers
71379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
72379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * Currently unimplemented checks include:
73379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - report ARP/ND data about on-link neighbors
74379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - DNS TCP queries to all DNS servers
75379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - HTTP DIRECT and PROXY checks
76379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - port 443 blocking/TLS intercept checks
77379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - QUIC reachability checks
78379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *     - MTU checks
79379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
80379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * The supplied timeout bounds the entire diagnostic process.  Each specific
81379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * check class must implement this upper bound on measurements in whichever
82379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * manner is most appropriate and effective.
83379747a4040118130ca35947aef6942b2cd1cd67Erik Kline *
84379747a4040118130ca35947aef6942b2cd1cd67Erik Kline * @hide
85379747a4040118130ca35947aef6942b2cd1cd67Erik Kline */
86379747a4040118130ca35947aef6942b2cd1cd67Erik Klinepublic class NetworkDiagnostics {
87379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static final String TAG = "NetworkDiagnostics";
88379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
8987cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti    private static final InetAddress TEST_DNS4 = NetworkUtils.numericToInetAddress("8.8.8.8");
9087cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti    private static final InetAddress TEST_DNS6 = NetworkUtils.numericToInetAddress(
9187cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti            "2001:4860:4860::8888");
9287cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti
93379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // For brevity elsewhere.
94379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static final long now() {
95379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        return SystemClock.elapsedRealtime();
96379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
97379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
98379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // Values from RFC 1035 section 4.1.1, names from <arpa/nameser.h>.
99379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // Should be a member of DnsUdpCheck, but "compiler says no".
100379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public static enum DnsResponseCode { NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMP, REFUSED };
101379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
102379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Network mNetwork;
103379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final LinkProperties mLinkProperties;
104379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Integer mInterfaceIndex;
105379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
106379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mTimeoutMs;
107379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mStartTime;
108379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final long mDeadlineTime;
109379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
110379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // A counter, initialized to the total number of measurements,
111379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    // so callers can wait for completion.
112379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final CountDownLatch mCountDownLatch;
113379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
114106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline    public class Measurement {
115379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final String SUCCEEDED = "SUCCEEDED";
116379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final String FAILED = "FAILED";
117379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
118106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        private boolean succeeded;
119379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
120106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        // Package private.  TODO: investigate better encapsulation.
121106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        String description = "";
122106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        long startTime;
123106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        long finishTime;
124106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        String result = "";
125106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        Thread thread;
126106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
127106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        public boolean checkSucceeded() { return succeeded; }
128106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
129106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        void recordSuccess(String msg) {
130379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            maybeFixupTimes();
131106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            succeeded = true;
132ed1b45968edb11ce8dc5c1d5a5db09d943e8d425Erik Kline            result = SUCCEEDED + ": " + msg;
133379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mCountDownLatch != null) {
134379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
135379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
136379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
137379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
138106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        void recordFailure(String msg) {
139379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            maybeFixupTimes();
140106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            succeeded = false;
141ed1b45968edb11ce8dc5c1d5a5db09d943e8d425Erik Kline            result = FAILED + ": " + msg;
142379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mCountDownLatch != null) {
143379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
144379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
145379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
146379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
147379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private void maybeFixupTimes() {
148379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Allows the caller to just set success/failure and not worry
149379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // about also setting the correct finishing time.
150379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (finishTime == 0) { finishTime = now(); }
151379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
152379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // In cases where, for example, a failure has occurred before the
153379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // measurement even began, fixup the start time to reflect as much.
154379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (startTime == 0) { startTime = finishTime; }
155379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
156379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
157379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
158379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public String toString() {
159379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return description + ": " + result + " (" + (finishTime - startTime) + "ms)";
160379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
161379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
162379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
163379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Map<InetAddress, Measurement> mIcmpChecks = new HashMap<>();
16429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti    private final Map<Pair<InetAddress, InetAddress>, Measurement> mExplicitSourceIcmpChecks =
16529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            new HashMap<>();
166379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final Map<InetAddress, Measurement> mDnsUdpChecks = new HashMap<>();
167379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private final String mDescription;
168379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
169379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
170379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public NetworkDiagnostics(Network network, LinkProperties lp, long timeoutMs) {
171379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mNetwork = network;
172379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mLinkProperties = lp;
173379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mInterfaceIndex = getInterfaceIndex(mLinkProperties.getInterfaceName());
174379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mTimeoutMs = timeoutMs;
175379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mStartTime = now();
176379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mDeadlineTime = mStartTime + mTimeoutMs;
177379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
17887cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // Hardcode measurements to TEST_DNS4 and TEST_DNS6 in order to test off-link connectivity.
17987cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // We are free to modify mLinkProperties with impunity because ConnectivityService passes us
18087cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // a copy and not the original object. It's easier to do it this way because we don't need
18187cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // to check whether the LinkProperties already contains these DNS servers because
18287cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // LinkProperties#addDnsServer checks for duplicates.
18387cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        if (mLinkProperties.isReachable(TEST_DNS4)) {
18487cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti            mLinkProperties.addDnsServer(TEST_DNS4);
18587cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        }
18687cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // TODO: we could use mLinkProperties.isReachable(TEST_DNS6) here, because we won't set any
18787cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // DNS servers for which isReachable() is false, but since this is diagnostic code, be extra
18887cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        // careful.
18987cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        if (mLinkProperties.hasGlobalIPv6Address() || mLinkProperties.hasIPv6DefaultRoute()) {
19087cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti            mLinkProperties.addDnsServer(TEST_DNS6);
19187cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti        }
19287cfc70b732422d6927ad9fe97f1ad7ab65fd508Lorenzo Colitti
193379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (RouteInfo route : mLinkProperties.getRoutes()) {
194379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (route.hasGateway()) {
19529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                InetAddress gateway = route.getGateway();
19629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                prepareIcmpMeasurement(gateway);
19729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                if (route.isIPv6Default()) {
19829f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                    prepareExplicitSourceIcmpMeasurements(gateway);
19929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                }
200379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
201379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
202379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (InetAddress nameserver : mLinkProperties.getDnsServers()) {
203379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                prepareIcmpMeasurement(nameserver);
204379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                prepareDnsMeasurement(nameserver);
205379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
206379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
207379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mCountDownLatch = new CountDownLatch(totalMeasurementCount());
208379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
209379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        startMeasurements();
210379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
211379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        mDescription = "ifaces{" + TextUtils.join(",", mLinkProperties.getAllInterfaceNames()) + "}"
212379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " index{" + mInterfaceIndex + "}"
213379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " network{" + mNetwork + "}"
214379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                + " nethandle{" + mNetwork.getNetworkHandle() + "}";
215379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
216379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
217379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private static Integer getInterfaceIndex(String ifname) {
218379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        try {
219379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            NetworkInterface ni = NetworkInterface.getByName(ifname);
220379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return ni.getIndex();
221379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        } catch (NullPointerException | SocketException e) {
222379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return null;
223379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
224379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
225379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
226379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void prepareIcmpMeasurement(InetAddress target) {
227379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        if (!mIcmpChecks.containsKey(target)) {
228379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Measurement measurement = new Measurement();
229379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread = new Thread(new IcmpCheck(target, measurement));
230379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mIcmpChecks.put(target, measurement);
231379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
232379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
233379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
23429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti    private void prepareExplicitSourceIcmpMeasurements(InetAddress target) {
23529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        for (LinkAddress l : mLinkProperties.getLinkAddresses()) {
23629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            InetAddress source = l.getAddress();
23729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            if (source instanceof Inet6Address && l.isGlobalPreferred()) {
23829f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                Pair<InetAddress, InetAddress> srcTarget = new Pair<>(source, target);
23929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                if (!mExplicitSourceIcmpChecks.containsKey(srcTarget)) {
24029f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                    Measurement measurement = new Measurement();
24129f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                    measurement.thread = new Thread(new IcmpCheck(source, target, measurement));
24229f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                    mExplicitSourceIcmpChecks.put(srcTarget, measurement);
24329f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                }
24429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            }
24529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        }
24629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti    }
24729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti
248379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void prepareDnsMeasurement(InetAddress target) {
249379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        if (!mDnsUdpChecks.containsKey(target)) {
250379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Measurement measurement = new Measurement();
251379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread = new Thread(new DnsUdpCheck(target, measurement));
252379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mDnsUdpChecks.put(target, measurement);
253379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
254379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
255379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
256379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private int totalMeasurementCount() {
25729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        return mIcmpChecks.size() + mExplicitSourceIcmpChecks.size() + mDnsUdpChecks.size();
258379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
259379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
260379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private void startMeasurements() {
261379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Measurement measurement : mIcmpChecks.values()) {
262379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread.start();
263379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
26429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        for (Measurement measurement : mExplicitSourceIcmpChecks.values()) {
26529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            measurement.thread.start();
26629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        }
267379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Measurement measurement : mDnsUdpChecks.values()) {
268379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            measurement.thread.start();
269379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
270379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
271379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
272379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    public void waitForMeasurements() {
273379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        try {
274379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mCountDownLatch.await(mDeadlineTime - now(), TimeUnit.MILLISECONDS);
275379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        } catch (InterruptedException ignored) {}
276379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
277379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
278106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline    public List<Measurement> getMeasurements() {
279106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        // TODO: Consider moving waitForMeasurements() in here to minimize the
280106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        // chance of caller errors.
281379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
282106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        ArrayList<Measurement> measurements = new ArrayList(totalMeasurementCount());
283106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
284106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        // Sort measurements IPv4 first.
285379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
286379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet4Address) {
287106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
288106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            }
289106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        }
290106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
291106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                mExplicitSourceIcmpChecks.entrySet()) {
292106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            if (entry.getKey().first instanceof Inet4Address) {
293106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
294106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            }
295106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        }
296106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
297106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            if (entry.getKey() instanceof Inet4Address) {
298106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
299379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
300379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
301106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
302106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        // IPv6 measurements second.
303379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mIcmpChecks.entrySet()) {
304379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet6Address) {
305106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
306379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
307379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
30829f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        for (Map.Entry<Pair<InetAddress, InetAddress>, Measurement> entry :
30929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                mExplicitSourceIcmpChecks.entrySet()) {
310106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            if (entry.getKey().first instanceof Inet6Address) {
311106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
312379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
313379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
314379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        for (Map.Entry<InetAddress, Measurement> entry : mDnsUdpChecks.entrySet()) {
315379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (entry.getKey() instanceof Inet6Address) {
316106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                measurements.add(entry.getValue());
317379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
318379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
319106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
320106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        return measurements;
321106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline    }
322106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
323106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline    public void dump(IndentingPrintWriter pw) {
324106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        pw.println(TAG + ":" + mDescription);
325106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        final long unfinished = mCountDownLatch.getCount();
326106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        if (unfinished > 0) {
327106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            // This can't happen unless a caller forgets to call waitForMeasurements()
328106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            // or a measurement isn't implemented to correctly honor the timeout.
329106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            pw.println("WARNING: countdown wait incomplete: "
330106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline                    + unfinished + " unfinished measurements");
331106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        }
332106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
333106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        pw.increaseIndent();
334106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
335106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        String prefix;
336106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        for (Measurement m : getMeasurements()) {
337106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            prefix = m.checkSucceeded() ? "." : "F";
338106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline            pw.println(prefix + "  " + m.toString());
339106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline        }
340106cdf6c94c455462316555b7ac59ec79c3b40faErik Kline
341379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        pw.decreaseIndent();
342379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
343379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
344379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
345379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class SimpleSocketCheck implements Closeable {
34629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        protected final InetAddress mSource;  // Usually null.
347379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final InetAddress mTarget;
348379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final int mAddressFamily;
349379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected final Measurement mMeasurement;
350379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected FileDescriptor mFileDescriptor;
351379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected SocketAddress mSocketAddress;
352379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
35329f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        protected SimpleSocketCheck(
35429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                InetAddress source, InetAddress target, Measurement measurement) {
355379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement = measurement;
356379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
357379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (target instanceof Inet6Address) {
358379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                Inet6Address targetWithScopeId = null;
359379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                if (target.isLinkLocalAddress() && mInterfaceIndex != null) {
360379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    try {
361379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        targetWithScopeId = Inet6Address.getByAddress(
362379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                                null, target.getAddress(), mInterfaceIndex);
363379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    } catch (UnknownHostException e) {
364379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        mMeasurement.recordFailure(e.toString());
365379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    }
366379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
367379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mTarget = (targetWithScopeId != null) ? targetWithScopeId : target;
368379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mAddressFamily = AF_INET6;
369379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
370379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mTarget = target;
371379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mAddressFamily = AF_INET;
372379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
37329f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti
37429f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            // We don't need to check the scope ID here because we currently only do explicit-source
37529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            // measurements from global IPv6 addresses.
37629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            mSource = source;
37729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        }
37829f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti
37929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        protected SimpleSocketCheck(InetAddress target, Measurement measurement) {
38029f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            this(null, target, measurement);
381379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
382379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
383379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected void setupSocket(
384379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                int sockType, int protocol, long writeTimeout, long readTimeout, int dstPort)
385379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                throws ErrnoException, IOException {
386619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey            final int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
387619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey            try {
388619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey                mFileDescriptor = Os.socket(mAddressFamily, sockType, protocol);
389619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey            } finally {
390619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey                TrafficStats.setThreadStatsTag(oldTag);
391619a511d4f5b5cc74b9a8ab853db386489e02e6fJeff Sharkey            }
392379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Setting SNDTIMEO is purely for defensive purposes.
393379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.setsockoptTimeval(mFileDescriptor,
394379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(writeTimeout));
395379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.setsockoptTimeval(mFileDescriptor,
396379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(readTimeout));
397379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // TODO: Use IP_RECVERR/IPV6_RECVERR, pending OsContants availability.
398379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mNetwork.bindSocket(mFileDescriptor);
39929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            if (mSource != null) {
40029f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti                Os.bind(mFileDescriptor, mSource, 0);
40129f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            }
402379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            Os.connect(mFileDescriptor, mTarget, dstPort);
403379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mSocketAddress = Os.getsockname(mFileDescriptor);
404379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
405379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
406379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        protected String getSocketAddressString() {
407379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // The default toString() implementation is not the prettiest.
408379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            InetSocketAddress inetSockAddr = (InetSocketAddress) mSocketAddress;
409379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            InetAddress localAddr = inetSockAddr.getAddress();
410379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return String.format(
411379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    (localAddr instanceof Inet6Address ? "[%s]:%d" : "%s:%d"),
412379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    localAddr.getHostAddress(), inetSockAddr.getPort());
413379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
414379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
415379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
416379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void close() {
417379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            IoUtils.closeQuietly(mFileDescriptor);
418379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
419379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
420379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
421379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
422379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class IcmpCheck extends SimpleSocketCheck implements Runnable {
423379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_SEND = 100;
424379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_RECV = 300;
425379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int PACKET_BUFSIZE = 512;
426379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mProtocol;
427379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mIcmpType;
428379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
42929f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        public IcmpCheck(InetAddress source, InetAddress target, Measurement measurement) {
43029f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            super(source, target, measurement);
431379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
432379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mAddressFamily == AF_INET6) {
433379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mProtocol = IPPROTO_ICMPV6;
434918a68b3ca3d375335691230cc8fd7c974403717Chalard Jean                mIcmpType = NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
435379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.description = "ICMPv6";
436379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
437379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mProtocol = IPPROTO_ICMP;
438918a68b3ca3d375335691230cc8fd7c974403717Chalard Jean                mIcmpType = NetworkConstants.ICMPV4_ECHO_REQUEST_TYPE;
439379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.description = "ICMPv4";
440379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
441379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
442379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " dst{" + mTarget.getHostAddress() + "}";
443379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
444379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
44529f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        public IcmpCheck(InetAddress target, Measurement measurement) {
44629f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti            this(null, target, measurement);
44729f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti        }
44829f666688d73dcaf3f65b8124f34841927f70186Lorenzo Colitti
449379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
450379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void run() {
451379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Check if this measurement has already failed during setup.
452379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime > 0) {
453379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // If the measurement failed during construction it didn't
454379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // decrement the countdown latch; do so here.
455379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
456379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
457379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
458379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
459379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
460379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                setupSocket(SOCK_DGRAM, mProtocol, TIMEOUT_SEND, TIMEOUT_RECV, 0);
461379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (ErrnoException | IOException e) {
462379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure(e.toString());
463379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
464379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
465379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " src{" + getSocketAddressString() + "}";
466379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
467379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Build a trivial ICMP packet.
468379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            final byte[] icmpPacket = {
469379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    (byte) mIcmpType, 0, 0, 0, 0, 0, 0, 0  // ICMP header
470379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            };
471379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
472379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            int count = 0;
473379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.startTime = now();
474379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            while (now() < mDeadlineTime - (TIMEOUT_SEND + TIMEOUT_RECV)) {
475379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                count++;
476379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                icmpPacket[icmpPacket.length - 1] = (byte) count;
477379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
478379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.write(mFileDescriptor, icmpPacket, 0, icmpPacket.length);
479379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
480379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordFailure(e.toString());
481379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
482379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
483379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
484379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
485379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
486379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.read(mFileDescriptor, reply);
487379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // TODO: send a few pings back to back to guesstimate packet loss.
488379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordSuccess("1/" + count);
489379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
490379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
491379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    continue;
492379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
493379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
494379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime == 0) {
495379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure("0/" + count);
496379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
497379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
498379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            close();
499379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
500379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
501379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
502379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
503379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    private class DnsUdpCheck extends SimpleSocketCheck implements Runnable {
504379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_SEND = 100;
505379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int TIMEOUT_RECV = 500;
506379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int RR_TYPE_A = 1;
507379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int RR_TYPE_AAAA = 28;
508379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private static final int PACKET_BUFSIZE = 512;
509379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
510379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final Random mRandom = new Random();
511379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
512379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        // Should be static, but the compiler mocks our puny, human attempts at reason.
513379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private String responseCodeStr(int rcode) {
514379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
515379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return DnsResponseCode.values()[rcode].toString();
516379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (IndexOutOfBoundsException e) {
517379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return String.valueOf(rcode);
518379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
519379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
520379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
521379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private final int mQueryType;
522379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
523379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public DnsUdpCheck(InetAddress target, Measurement measurement) {
524379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            super(target, measurement);
525379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
526379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // TODO: Ideally, query the target for both types regardless of address family.
527379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mAddressFamily == AF_INET6) {
528379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mQueryType = RR_TYPE_AAAA;
529379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } else {
530379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mQueryType = RR_TYPE_A;
531379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
532379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
533379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description = "DNS UDP dst{" + mTarget.getHostAddress() + "}";
534379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
535379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
536379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        @Override
537379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        public void run() {
538379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Check if this measurement has already failed during setup.
539379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime > 0) {
540379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // If the measurement failed during construction it didn't
541379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                // decrement the countdown latch; do so here.
542379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mCountDownLatch.countDown();
543379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
544379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
545379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
546379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            try {
547918a68b3ca3d375335691230cc8fd7c974403717Chalard Jean                setupSocket(SOCK_DGRAM, IPPROTO_UDP, TIMEOUT_SEND, TIMEOUT_RECV,
548918a68b3ca3d375335691230cc8fd7c974403717Chalard Jean                        NetworkConstants.DNS_SERVER_PORT);
549379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            } catch (ErrnoException | IOException e) {
550379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure(e.toString());
551379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                return;
552379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
553379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " src{" + getSocketAddressString() + "}";
554379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
555379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // This needs to be fixed length so it can be dropped into the pre-canned packet.
556a09b4d2a611a7606e8fc8c73a24bd941b6fc173fNarayan Kamath            final String sixRandomDigits = String.valueOf(mRandom.nextInt(900000) + 100000);
557379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.description += " qtype{" + mQueryType + "}"
558379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    + " qname{" + sixRandomDigits + "-android-ds.metric.gstatic.com}";
559379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
560379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            // Build a trivial DNS packet.
561379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            final byte[] dnsPacket = getDnsQueryPacket(sixRandomDigits);
562379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
563379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            int count = 0;
564379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            mMeasurement.startTime = now();
565379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            while (now() < mDeadlineTime - (TIMEOUT_RECV + TIMEOUT_RECV)) {
566379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                count++;
567379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
568379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.write(mFileDescriptor, dnsPacket, 0, dnsPacket.length);
569379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
570379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordFailure(e.toString());
571379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
572379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
573379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
574379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                try {
575379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    ByteBuffer reply = ByteBuffer.allocate(PACKET_BUFSIZE);
576379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    Os.read(mFileDescriptor, reply);
577379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // TODO: more correct and detailed evaluation of the response,
578379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    // possibly adding the returned IP address(es) to the output.
579379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    final String rcodeStr = (reply.limit() > 3)
580379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                            ? " " + responseCodeStr((int) (reply.get(3)) & 0x0f)
581379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                            : "";
582379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    mMeasurement.recordSuccess("1/" + count + rcodeStr);
583379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    break;
584379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                } catch (ErrnoException | InterruptedIOException e) {
585379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                    continue;
586379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                }
587379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
588379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            if (mMeasurement.finishTime == 0) {
589379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                mMeasurement.recordFailure("0/" + count);
590379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            }
591379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
592379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            close();
593379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
594379747a4040118130ca35947aef6942b2cd1cd67Erik Kline
595379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        private byte[] getDnsQueryPacket(String sixRandomDigits) {
596379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            byte[] rnd = sixRandomDigits.getBytes(StandardCharsets.US_ASCII);
597379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            return new byte[] {
598379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                (byte) mRandom.nextInt(), (byte) mRandom.nextInt(),  // [0-1]   query ID
599379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                1, 0,  // [2-3]   flags; byte[2] = 1 for recursion desired (RD).
600379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 1,  // [4-5]   QDCOUNT (number of queries)
601379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [6-7]   ANCOUNT (number of answers)
602379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [8-9]   NSCOUNT (number of name server records)
603379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 0,  // [10-11] ARCOUNT (number of additional records)
604379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                17, rnd[0], rnd[1], rnd[2], rnd[3], rnd[4], rnd[5],
605379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                        '-', 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 's',
606379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                6, 'm', 'e', 't', 'r', 'i', 'c',
607379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                7, 'g', 's', 't', 'a', 't', 'i', 'c',
608379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                3, 'c', 'o', 'm',
609379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0,  // null terminator of FQDN (root TLD)
610379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, (byte) mQueryType,  // QTYPE
611379747a4040118130ca35947aef6942b2cd1cd67Erik Kline                0, 1  // QCLASS, set to 1 = IN (Internet)
612379747a4040118130ca35947aef6942b2cd1cd67Erik Kline            };
613379747a4040118130ca35947aef6942b2cd1cd67Erik Kline        }
614379747a4040118130ca35947aef6942b2cd1cd67Erik Kline    }
615379747a4040118130ca35947aef6942b2cd1cd67Erik Kline}
616