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