1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.net; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.os.Parcel; 22 import android.os.Parcelable; 23 import android.text.TextUtils; 24 25 import java.net.Inet4Address; 26 import java.net.Inet6Address; 27 import java.net.InetAddress; 28 import java.net.UnknownHostException; 29 import java.util.ArrayList; 30 import java.util.Collection; 31 import java.util.Collections; 32 import java.util.Hashtable; 33 import java.util.List; 34 import java.util.Objects; 35 36 /** 37 * Describes the properties of a network link. 38 * 39 * A link represents a connection to a network. 40 * It may have multiple addresses and multiple gateways, 41 * multiple dns servers but only one http proxy and one 42 * network interface. 43 * 44 * Note that this is just a holder of data. Modifying it 45 * does not affect live networks. 46 * 47 */ 48 public final class LinkProperties implements Parcelable { 49 // The interface described by the network link. 50 private String mIfaceName; 51 private ArrayList<LinkAddress> mLinkAddresses = new ArrayList<LinkAddress>(); 52 private ArrayList<InetAddress> mDnses = new ArrayList<InetAddress>(); 53 private String mDomains; 54 private ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 55 private ProxyInfo mHttpProxy; 56 private int mMtu; 57 // in the format "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max" 58 private String mTcpBufferSizes; 59 60 private static final int MIN_MTU = 68; 61 private static final int MIN_MTU_V6 = 1280; 62 private static final int MAX_MTU = 10000; 63 64 // Stores the properties of links that are "stacked" above this link. 65 // Indexed by interface name to allow modification and to prevent duplicates being added. 66 private Hashtable<String, LinkProperties> mStackedLinks = 67 new Hashtable<String, LinkProperties>(); 68 69 /** 70 * @hide 71 */ 72 public static class CompareResult<T> { 73 public List<T> removed = new ArrayList<T>(); 74 public List<T> added = new ArrayList<T>(); 75 76 @Override toString()77 public String toString() { 78 String retVal = "removed=["; 79 for (T addr : removed) retVal += addr.toString() + ","; 80 retVal += "] added=["; 81 for (T addr : added) retVal += addr.toString() + ","; 82 retVal += "]"; 83 return retVal; 84 } 85 } 86 87 /** 88 * @hide 89 */ 90 public enum ProvisioningChange { 91 STILL_NOT_PROVISIONED, 92 LOST_PROVISIONING, 93 GAINED_PROVISIONING, 94 STILL_PROVISIONED, 95 } 96 97 /** 98 * Compare the provisioning states of two LinkProperties instances. 99 * 100 * @hide 101 */ compareProvisioning( LinkProperties before, LinkProperties after)102 public static ProvisioningChange compareProvisioning( 103 LinkProperties before, LinkProperties after) { 104 if (before.isProvisioned() && after.isProvisioned()) { 105 // On dualstack networks, DHCPv4 renewals can occasionally fail. 106 // When this happens, IPv6-reachable services continue to function 107 // normally but IPv4-only services (naturally) fail. 108 // 109 // When an application using an IPv4-only service reports a bad 110 // network condition to the framework, attempts to re-validate 111 // the network succeed (since we support IPv6-only networks) and 112 // nothing is changed. 113 // 114 // For users, this is confusing and unexpected behaviour, and is 115 // not necessarily easy to diagnose. Therefore, we treat changing 116 // from a dualstack network to an IPv6-only network equivalent to 117 // a total loss of provisioning. 118 // 119 // For one such example of this, see b/18867306. 120 // 121 // Additionally, losing IPv6 provisioning can result in TCP 122 // connections getting stuck until timeouts fire and other 123 // baffling failures. Therefore, loss of either IPv4 or IPv6 on a 124 // previously dualstack network is deemed a lost of provisioning. 125 if ((before.isIPv4Provisioned() && !after.isIPv4Provisioned()) || 126 (before.isIPv6Provisioned() && !after.isIPv6Provisioned())) { 127 return ProvisioningChange.LOST_PROVISIONING; 128 } 129 return ProvisioningChange.STILL_PROVISIONED; 130 } else if (before.isProvisioned() && !after.isProvisioned()) { 131 return ProvisioningChange.LOST_PROVISIONING; 132 } else if (!before.isProvisioned() && after.isProvisioned()) { 133 return ProvisioningChange.GAINED_PROVISIONING; 134 } else { // !before.isProvisioned() && !after.isProvisioned() 135 return ProvisioningChange.STILL_NOT_PROVISIONED; 136 } 137 } 138 139 /** 140 * @hide 141 */ LinkProperties()142 public LinkProperties() { 143 } 144 145 /** 146 * @hide 147 */ LinkProperties(LinkProperties source)148 public LinkProperties(LinkProperties source) { 149 if (source != null) { 150 mIfaceName = source.getInterfaceName(); 151 for (LinkAddress l : source.getLinkAddresses()) mLinkAddresses.add(l); 152 for (InetAddress i : source.getDnsServers()) mDnses.add(i); 153 mDomains = source.getDomains(); 154 for (RouteInfo r : source.getRoutes()) mRoutes.add(r); 155 mHttpProxy = (source.getHttpProxy() == null) ? 156 null : new ProxyInfo(source.getHttpProxy()); 157 for (LinkProperties l: source.mStackedLinks.values()) { 158 addStackedLink(l); 159 } 160 setMtu(source.getMtu()); 161 mTcpBufferSizes = source.mTcpBufferSizes; 162 } 163 } 164 165 /** 166 * Sets the interface name for this link. All {@link RouteInfo} already set for this 167 * will have their interface changed to match this new value. 168 * 169 * @param iface The name of the network interface used for this link. 170 * @hide 171 */ setInterfaceName(String iface)172 public void setInterfaceName(String iface) { 173 mIfaceName = iface; 174 ArrayList<RouteInfo> newRoutes = new ArrayList<RouteInfo>(mRoutes.size()); 175 for (RouteInfo route : mRoutes) { 176 newRoutes.add(routeWithInterface(route)); 177 } 178 mRoutes = newRoutes; 179 } 180 181 /** 182 * Gets the interface name for this link. May be {@code null} if not set. 183 * 184 * @return The interface name set for this link or {@code null}. 185 */ getInterfaceName()186 public @Nullable String getInterfaceName() { 187 return mIfaceName; 188 } 189 190 /** 191 * @hide 192 */ getAllInterfaceNames()193 public List<String> getAllInterfaceNames() { 194 List<String> interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1); 195 if (mIfaceName != null) interfaceNames.add(new String(mIfaceName)); 196 for (LinkProperties stacked: mStackedLinks.values()) { 197 interfaceNames.addAll(stacked.getAllInterfaceNames()); 198 } 199 return interfaceNames; 200 } 201 202 /** 203 * Returns all the addresses on this link. We often think of a link having a single address, 204 * however, particularly with Ipv6 several addresses are typical. Note that the 205 * {@code LinkProperties} actually contains {@link LinkAddress} objects which also include 206 * prefix lengths for each address. This is a simplified utility alternative to 207 * {@link LinkProperties#getLinkAddresses}. 208 * 209 * @return An umodifiable {@link List} of {@link InetAddress} for this link. 210 * @hide 211 */ getAddresses()212 public List<InetAddress> getAddresses() { 213 List<InetAddress> addresses = new ArrayList<InetAddress>(); 214 for (LinkAddress linkAddress : mLinkAddresses) { 215 addresses.add(linkAddress.getAddress()); 216 } 217 return Collections.unmodifiableList(addresses); 218 } 219 220 /** 221 * Returns all the addresses on this link and all the links stacked above it. 222 * @hide 223 */ getAllAddresses()224 public List<InetAddress> getAllAddresses() { 225 List<InetAddress> addresses = new ArrayList<InetAddress>(); 226 for (LinkAddress linkAddress : mLinkAddresses) { 227 addresses.add(linkAddress.getAddress()); 228 } 229 for (LinkProperties stacked: mStackedLinks.values()) { 230 addresses.addAll(stacked.getAllAddresses()); 231 } 232 return addresses; 233 } 234 findLinkAddressIndex(LinkAddress address)235 private int findLinkAddressIndex(LinkAddress address) { 236 for (int i = 0; i < mLinkAddresses.size(); i++) { 237 if (mLinkAddresses.get(i).isSameAddressAs(address)) { 238 return i; 239 } 240 } 241 return -1; 242 } 243 244 /** 245 * Adds a {@link LinkAddress} to this {@code LinkProperties} if a {@link LinkAddress} of the 246 * same address/prefix does not already exist. If it does exist it is replaced. 247 * @param address The {@code LinkAddress} to add. 248 * @return true if {@code address} was added or updated, false otherwise. 249 * @hide 250 */ addLinkAddress(LinkAddress address)251 public boolean addLinkAddress(LinkAddress address) { 252 if (address == null) { 253 return false; 254 } 255 int i = findLinkAddressIndex(address); 256 if (i < 0) { 257 // Address was not present. Add it. 258 mLinkAddresses.add(address); 259 return true; 260 } else if (mLinkAddresses.get(i).equals(address)) { 261 // Address was present and has same properties. Do nothing. 262 return false; 263 } else { 264 // Address was present and has different properties. Update it. 265 mLinkAddresses.set(i, address); 266 return true; 267 } 268 } 269 270 /** 271 * Removes a {@link LinkAddress} from this {@code LinkProperties}. Specifically, matches 272 * and {@link LinkAddress} with the same address and prefix. 273 * 274 * @param toRemove A {@link LinkAddress} specifying the address to remove. 275 * @return true if the address was removed, false if it did not exist. 276 * @hide 277 */ removeLinkAddress(LinkAddress toRemove)278 public boolean removeLinkAddress(LinkAddress toRemove) { 279 int i = findLinkAddressIndex(toRemove); 280 if (i >= 0) { 281 mLinkAddresses.remove(i); 282 return true; 283 } 284 return false; 285 } 286 287 /** 288 * Returns all the {@link LinkAddress} on this link. Typically a link will have 289 * one IPv4 address and one or more IPv6 addresses. 290 * 291 * @return An unmodifiable {@link List} of {@link LinkAddress} for this link. 292 */ getLinkAddresses()293 public List<LinkAddress> getLinkAddresses() { 294 return Collections.unmodifiableList(mLinkAddresses); 295 } 296 297 /** 298 * Returns all the addresses on this link and all the links stacked above it. 299 * @hide 300 */ getAllLinkAddresses()301 public List<LinkAddress> getAllLinkAddresses() { 302 List<LinkAddress> addresses = new ArrayList<LinkAddress>(); 303 addresses.addAll(mLinkAddresses); 304 for (LinkProperties stacked: mStackedLinks.values()) { 305 addresses.addAll(stacked.getAllLinkAddresses()); 306 } 307 return addresses; 308 } 309 310 /** 311 * Replaces the {@link LinkAddress} in this {@code LinkProperties} with 312 * the given {@link Collection} of {@link LinkAddress}. 313 * 314 * @param addresses The {@link Collection} of {@link LinkAddress} to set in this 315 * object. 316 * @hide 317 */ setLinkAddresses(Collection<LinkAddress> addresses)318 public void setLinkAddresses(Collection<LinkAddress> addresses) { 319 mLinkAddresses.clear(); 320 for (LinkAddress address: addresses) { 321 addLinkAddress(address); 322 } 323 } 324 325 /** 326 * Adds the given {@link InetAddress} to the list of DNS servers, if not present. 327 * 328 * @param dnsServer The {@link InetAddress} to add to the list of DNS servers. 329 * @return true if the DNS server was added, false if it was already present. 330 * @hide 331 */ addDnsServer(InetAddress dnsServer)332 public boolean addDnsServer(InetAddress dnsServer) { 333 if (dnsServer != null && !mDnses.contains(dnsServer)) { 334 mDnses.add(dnsServer); 335 return true; 336 } 337 return false; 338 } 339 340 /** 341 * Removes the given {@link InetAddress} from the list of DNS servers. 342 * 343 * @param dnsServer The {@link InetAddress} to remove from the list of DNS servers. 344 * @return true if the DNS server was removed, false if it did not exist. 345 * @hide 346 */ removeDnsServer(InetAddress dnsServer)347 public boolean removeDnsServer(InetAddress dnsServer) { 348 if (dnsServer != null) { 349 return mDnses.remove(dnsServer); 350 } 351 return false; 352 } 353 354 /** 355 * Replaces the DNS servers in this {@code LinkProperties} with 356 * the given {@link Collection} of {@link InetAddress} objects. 357 * 358 * @param addresses The {@link Collection} of DNS servers to set in this object. 359 * @hide 360 */ setDnsServers(Collection<InetAddress> dnsServers)361 public void setDnsServers(Collection<InetAddress> dnsServers) { 362 mDnses.clear(); 363 for (InetAddress dnsServer: dnsServers) { 364 addDnsServer(dnsServer); 365 } 366 } 367 368 /** 369 * Returns all the {@link InetAddress} for DNS servers on this link. 370 * 371 * @return An umodifiable {@link List} of {@link InetAddress} for DNS servers on 372 * this link. 373 */ getDnsServers()374 public List<InetAddress> getDnsServers() { 375 return Collections.unmodifiableList(mDnses); 376 } 377 378 /** 379 * Sets the DNS domain search path used on this link. 380 * 381 * @param domains A {@link String} listing in priority order the comma separated 382 * domains to search when resolving host names on this link. 383 * @hide 384 */ setDomains(String domains)385 public void setDomains(String domains) { 386 mDomains = domains; 387 } 388 389 /** 390 * Get the DNS domains search path set for this link. 391 * 392 * @return A {@link String} containing the comma separated domains to search when resolving 393 * host names on this link. 394 */ getDomains()395 public String getDomains() { 396 return mDomains; 397 } 398 399 /** 400 * Sets the Maximum Transmission Unit size to use on this link. This should not be used 401 * unless the system default (1500) is incorrect. Values less than 68 or greater than 402 * 10000 will be ignored. 403 * 404 * @param mtu The MTU to use for this link. 405 * @hide 406 */ setMtu(int mtu)407 public void setMtu(int mtu) { 408 mMtu = mtu; 409 } 410 411 /** 412 * Gets any non-default MTU size set for this link. Note that if the default is being used 413 * this will return 0. 414 * 415 * @return The mtu value set for this link. 416 * @hide 417 */ getMtu()418 public int getMtu() { 419 return mMtu; 420 } 421 422 /** 423 * Sets the tcp buffers sizes to be used when this link is the system default. 424 * Should be of the form "rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max". 425 * 426 * @param tcpBufferSizes The tcp buffers sizes to use. 427 * 428 * @hide 429 */ setTcpBufferSizes(String tcpBufferSizes)430 public void setTcpBufferSizes(String tcpBufferSizes) { 431 mTcpBufferSizes = tcpBufferSizes; 432 } 433 434 /** 435 * Gets the tcp buffer sizes. 436 * 437 * @return the tcp buffer sizes to use when this link is the system default. 438 * 439 * @hide 440 */ getTcpBufferSizes()441 public String getTcpBufferSizes() { 442 return mTcpBufferSizes; 443 } 444 routeWithInterface(RouteInfo route)445 private RouteInfo routeWithInterface(RouteInfo route) { 446 return new RouteInfo( 447 route.getDestination(), 448 route.getGateway(), 449 mIfaceName, 450 route.getType()); 451 } 452 453 /** 454 * Adds a {@link RouteInfo} to this {@code LinkProperties}, if not present. If the 455 * {@link RouteInfo} had an interface name set and that differs from the interface set for this 456 * {@code LinkProperties} an {@link IllegalArgumentException} will be thrown. The proper 457 * course is to add either un-named or properly named {@link RouteInfo}. 458 * 459 * @param route A {@link RouteInfo} to add to this object. 460 * @return {@code false} if the route was already present, {@code true} if it was added. 461 * 462 * @hide 463 */ addRoute(RouteInfo route)464 public boolean addRoute(RouteInfo route) { 465 if (route != null) { 466 String routeIface = route.getInterface(); 467 if (routeIface != null && !routeIface.equals(mIfaceName)) { 468 throw new IllegalArgumentException( 469 "Route added with non-matching interface: " + routeIface + 470 " vs. " + mIfaceName); 471 } 472 route = routeWithInterface(route); 473 if (!mRoutes.contains(route)) { 474 mRoutes.add(route); 475 return true; 476 } 477 } 478 return false; 479 } 480 481 /** 482 * Removes a {@link RouteInfo} from this {@code LinkProperties}, if present. The route must 483 * specify an interface and the interface must match the interface of this 484 * {@code LinkProperties}, or it will not be removed. 485 * 486 * @return {@code true} if the route was removed, {@code false} if it was not present. 487 * 488 * @hide 489 */ removeRoute(RouteInfo route)490 public boolean removeRoute(RouteInfo route) { 491 return route != null && 492 Objects.equals(mIfaceName, route.getInterface()) && 493 mRoutes.remove(route); 494 } 495 496 /** 497 * Returns all the {@link RouteInfo} set on this link. 498 * 499 * @return An unmodifiable {@link List} of {@link RouteInfo} for this link. 500 */ getRoutes()501 public List<RouteInfo> getRoutes() { 502 return Collections.unmodifiableList(mRoutes); 503 } 504 505 /** 506 * Make sure this LinkProperties instance contains routes that cover the local subnet 507 * of its link addresses. Add any route that is missing. 508 * @hide 509 */ ensureDirectlyConnectedRoutes()510 public void ensureDirectlyConnectedRoutes() { 511 for (LinkAddress addr: mLinkAddresses) { 512 addRoute(new RouteInfo(addr, null, mIfaceName)); 513 } 514 } 515 516 /** 517 * Returns all the routes on this link and all the links stacked above it. 518 * @hide 519 */ getAllRoutes()520 public List<RouteInfo> getAllRoutes() { 521 List<RouteInfo> routes = new ArrayList<>(); 522 routes.addAll(mRoutes); 523 for (LinkProperties stacked: mStackedLinks.values()) { 524 routes.addAll(stacked.getAllRoutes()); 525 } 526 return routes; 527 } 528 529 /** 530 * Sets the recommended {@link ProxyInfo} to use on this link, or {@code null} for none. 531 * Note that Http Proxies are only a hint - the system recommends their use, but it does 532 * not enforce it and applications may ignore them. 533 * 534 * @param proxy A {@link ProxyInfo} defining the HTTP Proxy to use on this link. 535 * @hide 536 */ setHttpProxy(ProxyInfo proxy)537 public void setHttpProxy(ProxyInfo proxy) { 538 mHttpProxy = proxy; 539 } 540 541 /** 542 * Gets the recommended {@link ProxyInfo} (or {@code null}) set on this link. 543 * 544 * @return The {@link ProxyInfo} set on this link 545 */ getHttpProxy()546 public ProxyInfo getHttpProxy() { 547 return mHttpProxy; 548 } 549 550 /** 551 * Adds a stacked link. 552 * 553 * If there is already a stacked link with the same interfacename as link, 554 * that link is replaced with link. Otherwise, link is added to the list 555 * of stacked links. If link is null, nothing changes. 556 * 557 * @param link The link to add. 558 * @return true if the link was stacked, false otherwise. 559 * @hide 560 */ addStackedLink(LinkProperties link)561 public boolean addStackedLink(LinkProperties link) { 562 if (link != null && link.getInterfaceName() != null) { 563 mStackedLinks.put(link.getInterfaceName(), link); 564 return true; 565 } 566 return false; 567 } 568 569 /** 570 * Removes a stacked link. 571 * 572 * If there is a stacked link with the given interface name, it is 573 * removed. Otherwise, nothing changes. 574 * 575 * @param iface The interface name of the link to remove. 576 * @return true if the link was removed, false otherwise. 577 * @hide 578 */ removeStackedLink(String iface)579 public boolean removeStackedLink(String iface) { 580 if (iface != null) { 581 LinkProperties removed = mStackedLinks.remove(iface); 582 return removed != null; 583 } 584 return false; 585 } 586 587 /** 588 * Returns all the links stacked on top of this link. 589 * @hide 590 */ getStackedLinks()591 public @NonNull List<LinkProperties> getStackedLinks() { 592 if (mStackedLinks.isEmpty()) { 593 return Collections.EMPTY_LIST; 594 } 595 List<LinkProperties> stacked = new ArrayList<LinkProperties>(); 596 for (LinkProperties link : mStackedLinks.values()) { 597 stacked.add(new LinkProperties(link)); 598 } 599 return Collections.unmodifiableList(stacked); 600 } 601 602 /** 603 * Clears this object to its initial state. 604 * @hide 605 */ clear()606 public void clear() { 607 mIfaceName = null; 608 mLinkAddresses.clear(); 609 mDnses.clear(); 610 mDomains = null; 611 mRoutes.clear(); 612 mHttpProxy = null; 613 mStackedLinks.clear(); 614 mMtu = 0; 615 mTcpBufferSizes = null; 616 } 617 618 /** 619 * Implement the Parcelable interface 620 */ describeContents()621 public int describeContents() { 622 return 0; 623 } 624 625 @Override toString()626 public String toString() { 627 String ifaceName = (mIfaceName == null ? "" : "InterfaceName: " + mIfaceName + " "); 628 629 String linkAddresses = "LinkAddresses: ["; 630 for (LinkAddress addr : mLinkAddresses) linkAddresses += addr.toString() + ","; 631 linkAddresses += "] "; 632 633 String dns = "DnsAddresses: ["; 634 for (InetAddress addr : mDnses) dns += addr.getHostAddress() + ","; 635 dns += "] "; 636 637 String domainName = "Domains: " + mDomains; 638 639 String mtu = " MTU: " + mMtu; 640 641 String tcpBuffSizes = ""; 642 if (mTcpBufferSizes != null) { 643 tcpBuffSizes = " TcpBufferSizes: " + mTcpBufferSizes; 644 } 645 646 String routes = " Routes: ["; 647 for (RouteInfo route : mRoutes) routes += route.toString() + ","; 648 routes += "] "; 649 String proxy = (mHttpProxy == null ? "" : " HttpProxy: " + mHttpProxy.toString() + " "); 650 651 String stacked = ""; 652 if (mStackedLinks.values().size() > 0) { 653 stacked += " Stacked: ["; 654 for (LinkProperties link: mStackedLinks.values()) { 655 stacked += " [" + link.toString() + " ],"; 656 } 657 stacked += "] "; 658 } 659 return "{" + ifaceName + linkAddresses + routes + dns + domainName + mtu 660 + tcpBuffSizes + proxy + stacked + "}"; 661 } 662 663 /** 664 * Returns true if this link has an IPv4 address. 665 * 666 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 667 * @hide 668 */ hasIPv4Address()669 public boolean hasIPv4Address() { 670 for (LinkAddress address : mLinkAddresses) { 671 if (address.getAddress() instanceof Inet4Address) { 672 return true; 673 } 674 } 675 return false; 676 } 677 678 /** 679 * Returns true if this link or any of its stacked interfaces has an IPv4 address. 680 * 681 * @return {@code true} if there is an IPv4 address, {@code false} otherwise. 682 */ hasIPv4AddressOnInterface(String iface)683 private boolean hasIPv4AddressOnInterface(String iface) { 684 // mIfaceName can be null. 685 return (Objects.equals(iface, mIfaceName) && hasIPv4Address()) || 686 (iface != null && mStackedLinks.containsKey(iface) && 687 mStackedLinks.get(iface).hasIPv4Address()); 688 } 689 690 /** 691 * Returns true if this link has a global preferred IPv6 address. 692 * 693 * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. 694 * @hide 695 */ hasGlobalIPv6Address()696 public boolean hasGlobalIPv6Address() { 697 for (LinkAddress address : mLinkAddresses) { 698 if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) { 699 return true; 700 } 701 } 702 return false; 703 } 704 705 /** 706 * Returns true if this link has an IPv4 default route. 707 * 708 * @return {@code true} if there is an IPv4 default route, {@code false} otherwise. 709 * @hide 710 */ hasIPv4DefaultRoute()711 public boolean hasIPv4DefaultRoute() { 712 for (RouteInfo r : mRoutes) { 713 if (r.isIPv4Default()) { 714 return true; 715 } 716 } 717 return false; 718 } 719 720 /** 721 * Returns true if this link has an IPv6 default route. 722 * 723 * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. 724 * @hide 725 */ hasIPv6DefaultRoute()726 public boolean hasIPv6DefaultRoute() { 727 for (RouteInfo r : mRoutes) { 728 if (r.isIPv6Default()) { 729 return true; 730 } 731 } 732 return false; 733 } 734 735 /** 736 * Returns true if this link has an IPv4 DNS server. 737 * 738 * @return {@code true} if there is an IPv4 DNS server, {@code false} otherwise. 739 * @hide 740 */ hasIPv4DnsServer()741 public boolean hasIPv4DnsServer() { 742 for (InetAddress ia : mDnses) { 743 if (ia instanceof Inet4Address) { 744 return true; 745 } 746 } 747 return false; 748 } 749 750 /** 751 * Returns true if this link has an IPv6 DNS server. 752 * 753 * @return {@code true} if there is an IPv6 DNS server, {@code false} otherwise. 754 * @hide 755 */ hasIPv6DnsServer()756 public boolean hasIPv6DnsServer() { 757 for (InetAddress ia : mDnses) { 758 if (ia instanceof Inet6Address) { 759 return true; 760 } 761 } 762 return false; 763 } 764 765 /** 766 * Returns true if this link is provisioned for global IPv4 connectivity. 767 * This requires an IP address, default route, and DNS server. 768 * 769 * @return {@code true} if the link is provisioned, {@code false} otherwise. 770 * @hide 771 */ isIPv4Provisioned()772 public boolean isIPv4Provisioned() { 773 return (hasIPv4Address() && 774 hasIPv4DefaultRoute() && 775 hasIPv4DnsServer()); 776 } 777 778 /** 779 * Returns true if this link is provisioned for global IPv6 connectivity. 780 * This requires an IP address, default route, and DNS server. 781 * 782 * @return {@code true} if the link is provisioned, {@code false} otherwise. 783 * @hide 784 */ isIPv6Provisioned()785 public boolean isIPv6Provisioned() { 786 return (hasGlobalIPv6Address() && 787 hasIPv6DefaultRoute() && 788 hasIPv6DnsServer()); 789 } 790 791 /** 792 * Returns true if this link is provisioned for global connectivity, 793 * for at least one Internet Protocol family. 794 * 795 * @return {@code true} if the link is provisioned, {@code false} otherwise. 796 * @hide 797 */ isProvisioned()798 public boolean isProvisioned() { 799 return (isIPv4Provisioned() || isIPv6Provisioned()); 800 } 801 802 /** 803 * Evaluate whether the {@link InetAddress} is considered reachable. 804 * 805 * @return {@code true} if the given {@link InetAddress} is considered reachable, 806 * {@code false} otherwise. 807 * @hide 808 */ isReachable(InetAddress ip)809 public boolean isReachable(InetAddress ip) { 810 final List<RouteInfo> allRoutes = getAllRoutes(); 811 // If we don't have a route to this IP address, it's not reachable. 812 final RouteInfo bestRoute = RouteInfo.selectBestRoute(allRoutes, ip); 813 if (bestRoute == null) { 814 return false; 815 } 816 817 // TODO: better source address evaluation for destination addresses. 818 819 if (ip instanceof Inet4Address) { 820 // For IPv4, it suffices for now to simply have any address. 821 return hasIPv4AddressOnInterface(bestRoute.getInterface()); 822 } else if (ip instanceof Inet6Address) { 823 if (ip.isLinkLocalAddress()) { 824 // For now, just make sure link-local destinations have 825 // scopedIds set, since transmits will generally fail otherwise. 826 // TODO: verify it matches the ifindex of one of the interfaces. 827 return (((Inet6Address)ip).getScopeId() != 0); 828 } else { 829 // For non-link-local destinations check that either the best route 830 // is directly connected or that some global preferred address exists. 831 // TODO: reconsider all cases (disconnected ULA networks, ...). 832 return (!bestRoute.hasGateway() || hasGlobalIPv6Address()); 833 } 834 } 835 836 return false; 837 } 838 839 /** 840 * Compares this {@code LinkProperties} interface name against the target 841 * 842 * @param target LinkProperties to compare. 843 * @return {@code true} if both are identical, {@code false} otherwise. 844 * @hide 845 */ isIdenticalInterfaceName(LinkProperties target)846 public boolean isIdenticalInterfaceName(LinkProperties target) { 847 return TextUtils.equals(getInterfaceName(), target.getInterfaceName()); 848 } 849 850 /** 851 * Compares this {@code LinkProperties} interface addresses against the target 852 * 853 * @param target LinkProperties to compare. 854 * @return {@code true} if both are identical, {@code false} otherwise. 855 * @hide 856 */ isIdenticalAddresses(LinkProperties target)857 public boolean isIdenticalAddresses(LinkProperties target) { 858 Collection<InetAddress> targetAddresses = target.getAddresses(); 859 Collection<InetAddress> sourceAddresses = getAddresses(); 860 return (sourceAddresses.size() == targetAddresses.size()) ? 861 sourceAddresses.containsAll(targetAddresses) : false; 862 } 863 864 /** 865 * Compares this {@code LinkProperties} DNS addresses against the target 866 * 867 * @param target LinkProperties to compare. 868 * @return {@code true} if both are identical, {@code false} otherwise. 869 * @hide 870 */ isIdenticalDnses(LinkProperties target)871 public boolean isIdenticalDnses(LinkProperties target) { 872 Collection<InetAddress> targetDnses = target.getDnsServers(); 873 String targetDomains = target.getDomains(); 874 if (mDomains == null) { 875 if (targetDomains != null) return false; 876 } else { 877 if (mDomains.equals(targetDomains) == false) return false; 878 } 879 return (mDnses.size() == targetDnses.size()) ? 880 mDnses.containsAll(targetDnses) : false; 881 } 882 883 /** 884 * Compares this {@code LinkProperties} Routes against the target 885 * 886 * @param target LinkProperties to compare. 887 * @return {@code true} if both are identical, {@code false} otherwise. 888 * @hide 889 */ isIdenticalRoutes(LinkProperties target)890 public boolean isIdenticalRoutes(LinkProperties target) { 891 Collection<RouteInfo> targetRoutes = target.getRoutes(); 892 return (mRoutes.size() == targetRoutes.size()) ? 893 mRoutes.containsAll(targetRoutes) : false; 894 } 895 896 /** 897 * Compares this {@code LinkProperties} HttpProxy against the target 898 * 899 * @param target LinkProperties to compare. 900 * @return {@code true} if both are identical, {@code false} otherwise. 901 * @hide 902 */ isIdenticalHttpProxy(LinkProperties target)903 public boolean isIdenticalHttpProxy(LinkProperties target) { 904 return getHttpProxy() == null ? target.getHttpProxy() == null : 905 getHttpProxy().equals(target.getHttpProxy()); 906 } 907 908 /** 909 * Compares this {@code LinkProperties} stacked links against the target 910 * 911 * @param target LinkProperties to compare. 912 * @return {@code true} if both are identical, {@code false} otherwise. 913 * @hide 914 */ isIdenticalStackedLinks(LinkProperties target)915 public boolean isIdenticalStackedLinks(LinkProperties target) { 916 if (!mStackedLinks.keySet().equals(target.mStackedLinks.keySet())) { 917 return false; 918 } 919 for (LinkProperties stacked : mStackedLinks.values()) { 920 // Hashtable values can never be null. 921 String iface = stacked.getInterfaceName(); 922 if (!stacked.equals(target.mStackedLinks.get(iface))) { 923 return false; 924 } 925 } 926 return true; 927 } 928 929 /** 930 * Compares this {@code LinkProperties} MTU against the target 931 * 932 * @param target LinkProperties to compare. 933 * @return {@code true} if both are identical, {@code false} otherwise. 934 * @hide 935 */ isIdenticalMtu(LinkProperties target)936 public boolean isIdenticalMtu(LinkProperties target) { 937 return getMtu() == target.getMtu(); 938 } 939 940 /** 941 * Compares this {@code LinkProperties} Tcp buffer sizes against the target. 942 * 943 * @param target LinkProperties to compare. 944 * @return {@code true} if both are identical, {@code false} otherwise. 945 * @hide 946 */ isIdenticalTcpBufferSizes(LinkProperties target)947 public boolean isIdenticalTcpBufferSizes(LinkProperties target) { 948 return Objects.equals(mTcpBufferSizes, target.mTcpBufferSizes); 949 } 950 951 @Override 952 /** 953 * Compares this {@code LinkProperties} instance against the target 954 * LinkProperties in {@code obj}. Two LinkPropertieses are equal if 955 * all their fields are equal in values. 956 * 957 * For collection fields, such as mDnses, containsAll() is used to check 958 * if two collections contains the same elements, independent of order. 959 * There are two thoughts regarding containsAll() 960 * 1. Duplicated elements. eg, (A, B, B) and (A, A, B) are equal. 961 * 2. Worst case performance is O(n^2). 962 * 963 * @param obj the object to be tested for equality. 964 * @return {@code true} if both objects are equal, {@code false} otherwise. 965 */ equals(Object obj)966 public boolean equals(Object obj) { 967 if (this == obj) return true; 968 969 if (!(obj instanceof LinkProperties)) return false; 970 971 LinkProperties target = (LinkProperties) obj; 972 /** 973 * This method does not check that stacked interfaces are equal, because 974 * stacked interfaces are not so much a property of the link as a 975 * description of connections between links. 976 */ 977 return isIdenticalInterfaceName(target) && 978 isIdenticalAddresses(target) && 979 isIdenticalDnses(target) && 980 isIdenticalRoutes(target) && 981 isIdenticalHttpProxy(target) && 982 isIdenticalStackedLinks(target) && 983 isIdenticalMtu(target) && 984 isIdenticalTcpBufferSizes(target); 985 } 986 987 /** 988 * Compares the addresses in this LinkProperties with another 989 * LinkProperties, examining only addresses on the base link. 990 * 991 * @param target a LinkProperties with the new list of addresses 992 * @return the differences between the addresses. 993 * @hide 994 */ compareAddresses(LinkProperties target)995 public CompareResult<LinkAddress> compareAddresses(LinkProperties target) { 996 /* 997 * Duplicate the LinkAddresses into removed, we will be removing 998 * address which are common between mLinkAddresses and target 999 * leaving the addresses that are different. And address which 1000 * are in target but not in mLinkAddresses are placed in the 1001 * addedAddresses. 1002 */ 1003 CompareResult<LinkAddress> result = new CompareResult<LinkAddress>(); 1004 result.removed = new ArrayList<LinkAddress>(mLinkAddresses); 1005 result.added.clear(); 1006 if (target != null) { 1007 for (LinkAddress newAddress : target.getLinkAddresses()) { 1008 if (! result.removed.remove(newAddress)) { 1009 result.added.add(newAddress); 1010 } 1011 } 1012 } 1013 return result; 1014 } 1015 1016 /** 1017 * Compares the DNS addresses in this LinkProperties with another 1018 * LinkProperties, examining only DNS addresses on the base link. 1019 * 1020 * @param target a LinkProperties with the new list of dns addresses 1021 * @return the differences between the DNS addresses. 1022 * @hide 1023 */ compareDnses(LinkProperties target)1024 public CompareResult<InetAddress> compareDnses(LinkProperties target) { 1025 /* 1026 * Duplicate the InetAddresses into removed, we will be removing 1027 * dns address which are common between mDnses and target 1028 * leaving the addresses that are different. And dns address which 1029 * are in target but not in mDnses are placed in the 1030 * addedAddresses. 1031 */ 1032 CompareResult<InetAddress> result = new CompareResult<InetAddress>(); 1033 1034 result.removed = new ArrayList<InetAddress>(mDnses); 1035 result.added.clear(); 1036 if (target != null) { 1037 for (InetAddress newAddress : target.getDnsServers()) { 1038 if (! result.removed.remove(newAddress)) { 1039 result.added.add(newAddress); 1040 } 1041 } 1042 } 1043 return result; 1044 } 1045 1046 /** 1047 * Compares all routes in this LinkProperties with another LinkProperties, 1048 * examining both the the base link and all stacked links. 1049 * 1050 * @param target a LinkProperties with the new list of routes 1051 * @return the differences between the routes. 1052 * @hide 1053 */ compareAllRoutes(LinkProperties target)1054 public CompareResult<RouteInfo> compareAllRoutes(LinkProperties target) { 1055 /* 1056 * Duplicate the RouteInfos into removed, we will be removing 1057 * routes which are common between mRoutes and target 1058 * leaving the routes that are different. And route address which 1059 * are in target but not in mRoutes are placed in added. 1060 */ 1061 CompareResult<RouteInfo> result = new CompareResult<RouteInfo>(); 1062 1063 result.removed = getAllRoutes(); 1064 result.added.clear(); 1065 if (target != null) { 1066 for (RouteInfo r : target.getAllRoutes()) { 1067 if (! result.removed.remove(r)) { 1068 result.added.add(r); 1069 } 1070 } 1071 } 1072 return result; 1073 } 1074 1075 /** 1076 * Compares all interface names in this LinkProperties with another 1077 * LinkProperties, examining both the the base link and all stacked links. 1078 * 1079 * @param target a LinkProperties with the new list of interface names 1080 * @return the differences between the interface names. 1081 * @hide 1082 */ compareAllInterfaceNames(LinkProperties target)1083 public CompareResult<String> compareAllInterfaceNames(LinkProperties target) { 1084 /* 1085 * Duplicate the interface names into removed, we will be removing 1086 * interface names which are common between this and target 1087 * leaving the interface names that are different. And interface names which 1088 * are in target but not in this are placed in added. 1089 */ 1090 CompareResult<String> result = new CompareResult<String>(); 1091 1092 result.removed = getAllInterfaceNames(); 1093 result.added.clear(); 1094 if (target != null) { 1095 for (String r : target.getAllInterfaceNames()) { 1096 if (! result.removed.remove(r)) { 1097 result.added.add(r); 1098 } 1099 } 1100 } 1101 return result; 1102 } 1103 1104 1105 @Override 1106 /** 1107 * generate hashcode based on significant fields 1108 * Equal objects must produce the same hash code, while unequal objects 1109 * may have the same hash codes. 1110 */ hashCode()1111 public int hashCode() { 1112 return ((null == mIfaceName) ? 0 : mIfaceName.hashCode() 1113 + mLinkAddresses.size() * 31 1114 + mDnses.size() * 37 1115 + ((null == mDomains) ? 0 : mDomains.hashCode()) 1116 + mRoutes.size() * 41 1117 + ((null == mHttpProxy) ? 0 : mHttpProxy.hashCode()) 1118 + mStackedLinks.hashCode() * 47) 1119 + mMtu * 51 1120 + ((null == mTcpBufferSizes) ? 0 : mTcpBufferSizes.hashCode()); 1121 } 1122 1123 /** 1124 * Implement the Parcelable interface. 1125 */ writeToParcel(Parcel dest, int flags)1126 public void writeToParcel(Parcel dest, int flags) { 1127 dest.writeString(getInterfaceName()); 1128 dest.writeInt(mLinkAddresses.size()); 1129 for(LinkAddress linkAddress : mLinkAddresses) { 1130 dest.writeParcelable(linkAddress, flags); 1131 } 1132 1133 dest.writeInt(mDnses.size()); 1134 for(InetAddress d : mDnses) { 1135 dest.writeByteArray(d.getAddress()); 1136 } 1137 dest.writeString(mDomains); 1138 dest.writeInt(mMtu); 1139 dest.writeString(mTcpBufferSizes); 1140 dest.writeInt(mRoutes.size()); 1141 for(RouteInfo route : mRoutes) { 1142 dest.writeParcelable(route, flags); 1143 } 1144 1145 if (mHttpProxy != null) { 1146 dest.writeByte((byte)1); 1147 dest.writeParcelable(mHttpProxy, flags); 1148 } else { 1149 dest.writeByte((byte)0); 1150 } 1151 ArrayList<LinkProperties> stackedLinks = new ArrayList(mStackedLinks.values()); 1152 dest.writeList(stackedLinks); 1153 } 1154 1155 /** 1156 * Implement the Parcelable interface. 1157 */ 1158 public static final Creator<LinkProperties> CREATOR = 1159 new Creator<LinkProperties>() { 1160 public LinkProperties createFromParcel(Parcel in) { 1161 LinkProperties netProp = new LinkProperties(); 1162 1163 String iface = in.readString(); 1164 if (iface != null) { 1165 netProp.setInterfaceName(iface); 1166 } 1167 int addressCount = in.readInt(); 1168 for (int i=0; i<addressCount; i++) { 1169 netProp.addLinkAddress((LinkAddress)in.readParcelable(null)); 1170 } 1171 addressCount = in.readInt(); 1172 for (int i=0; i<addressCount; i++) { 1173 try { 1174 netProp.addDnsServer(InetAddress.getByAddress(in.createByteArray())); 1175 } catch (UnknownHostException e) { } 1176 } 1177 netProp.setDomains(in.readString()); 1178 netProp.setMtu(in.readInt()); 1179 netProp.setTcpBufferSizes(in.readString()); 1180 addressCount = in.readInt(); 1181 for (int i=0; i<addressCount; i++) { 1182 netProp.addRoute((RouteInfo)in.readParcelable(null)); 1183 } 1184 if (in.readByte() == 1) { 1185 netProp.setHttpProxy((ProxyInfo)in.readParcelable(null)); 1186 } 1187 ArrayList<LinkProperties> stackedLinks = new ArrayList<LinkProperties>(); 1188 in.readList(stackedLinks, LinkProperties.class.getClassLoader()); 1189 for (LinkProperties stackedLink: stackedLinks) { 1190 netProp.addStackedLink(stackedLink); 1191 } 1192 return netProp; 1193 } 1194 1195 public LinkProperties[] newArray(int size) { 1196 return new LinkProperties[size]; 1197 } 1198 }; 1199 1200 /** 1201 * Check the valid MTU range based on IPv4 or IPv6. 1202 * @hide 1203 */ isValidMtu(int mtu, boolean ipv6)1204 public static boolean isValidMtu(int mtu, boolean ipv6) { 1205 if (ipv6) { 1206 if ((mtu >= MIN_MTU_V6 && mtu <= MAX_MTU)) return true; 1207 } else { 1208 if ((mtu >= MIN_MTU && mtu <= MAX_MTU)) return true; 1209 } 1210 return false; 1211 } 1212 } 1213