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