1 /* 2 * Copyright (C) 2014 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.ip; 18 19 import static android.system.OsConstants.AF_INET6; 20 21 import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; 22 23 import android.app.AlarmManager; 24 import android.content.Context; 25 import android.net.InetAddresses; 26 import android.net.IpPrefix; 27 import android.net.LinkAddress; 28 import android.net.LinkProperties; 29 import android.net.RouteInfo; 30 import android.net.netlink.NduseroptMessage; 31 import android.net.netlink.NetlinkConstants; 32 import android.net.netlink.NetlinkMessage; 33 import android.net.netlink.StructNdOptPref64; 34 import android.net.util.InterfaceParams; 35 import android.net.util.SharedLog; 36 import android.os.Handler; 37 import android.system.OsConstants; 38 import android.util.Log; 39 40 import com.android.networkstack.apishim.NetworkInformationShimImpl; 41 import com.android.networkstack.apishim.common.NetworkInformationShim; 42 import com.android.server.NetworkObserver; 43 44 import java.net.InetAddress; 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Collections; 48 import java.util.HashMap; 49 import java.util.HashSet; 50 import java.util.Set; 51 import java.util.concurrent.TimeUnit; 52 53 /** 54 * Keeps track of link configuration received from Netd. 55 * 56 * An instance of this class is constructed by passing in an interface name and a callback. The 57 * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the 58 * class receives update notifications, it applies the update to its local LinkProperties, and if 59 * something has changed, notifies its owner of the update via the callback. 60 * 61 * The owner can then call {@code getLinkProperties()} in order to find out 62 * what changed. If in the meantime the LinkProperties stored here have changed, 63 * this class will return the current LinkProperties. Because each change 64 * triggers an update callback after the change is made, the owner may get more 65 * callbacks than strictly necessary (some of which may be no-ops), but will not 66 * be out of sync once all callbacks have been processed. 67 * 68 * Threading model: 69 * 70 * - The owner of this class is expected to create it, register it, and call 71 * getLinkProperties or clearLinkProperties on its thread. 72 * - Most of the methods in the class are implementing NetworkObserver and are called 73 * on the handler used to register the observer. 74 * - All accesses to mLinkProperties must be synchronized(this). All the other 75 * member variables are immutable once the object is constructed. 76 * 77 * TODO: Now that all the methods are called on the handler thread, remove synchronization and 78 * pass the LinkProperties to the update() callback. 79 * TODO: Stop extending NetworkObserver and get events from netlink directly. 80 * 81 * @hide 82 */ 83 public class IpClientLinkObserver implements NetworkObserver { 84 private final String mTag; 85 86 /** 87 * Callback used by {@link IpClientLinkObserver} to send update notifications. 88 */ 89 public interface Callback { 90 /** 91 * Called when some properties of the link were updated. 92 * 93 * @param linkState Whether the interface link state is up as per the latest 94 * {@link #onInterfaceLinkStateChanged(String, boolean)} callback. This 95 * should only be used for metrics purposes, as it could be inconsistent 96 * with {@link #getLinkProperties()} in particular. 97 */ update(boolean linkState)98 void update(boolean linkState); 99 } 100 101 /** Configuration parameters for IpClientLinkObserver. */ 102 public static class Configuration { 103 public final int minRdnssLifetime; 104 Configuration(int minRdnssLifetime)105 public Configuration(int minRdnssLifetime) { 106 this.minRdnssLifetime = minRdnssLifetime; 107 } 108 } 109 110 private final String mInterfaceName; 111 private final Callback mCallback; 112 private final LinkProperties mLinkProperties; 113 private boolean mInterfaceLinkState; 114 private DnsServerRepository mDnsServerRepository; 115 private final AlarmManager mAlarmManager; 116 private final Configuration mConfig; 117 private final Handler mHandler; 118 119 private final MyNetlinkMonitor mNetlinkMonitor; 120 121 private static final boolean DBG = false; 122 IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, Configuration config, SharedLog log)123 public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, 124 Configuration config, SharedLog log) { 125 mInterfaceName = iface; 126 mTag = "NetlinkTracker/" + mInterfaceName; 127 mCallback = callback; 128 mLinkProperties = new LinkProperties(); 129 mLinkProperties.setInterfaceName(mInterfaceName); 130 mConfig = config; 131 mHandler = h; 132 mInterfaceLinkState = true; // Assume up by default 133 mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime); 134 mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 135 mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); 136 mHandler.post(mNetlinkMonitor::start); 137 } 138 shutdown()139 public void shutdown() { 140 mHandler.post(mNetlinkMonitor::stop); 141 } 142 maybeLog(String operation, String iface, LinkAddress address)143 private void maybeLog(String operation, String iface, LinkAddress address) { 144 if (DBG) { 145 Log.d(mTag, operation + ": " + address + " on " + iface 146 + " flags " + address.getFlags() + " scope " + address.getScope()); 147 } 148 } 149 maybeLog(String operation, Object o)150 private void maybeLog(String operation, Object o) { 151 if (DBG) { 152 Log.d(mTag, operation + ": " + o.toString()); 153 } 154 } 155 156 @Override onInterfaceRemoved(String iface)157 public void onInterfaceRemoved(String iface) { 158 maybeLog("interfaceRemoved", iface); 159 if (mInterfaceName.equals(iface)) { 160 // Our interface was removed. Clear our LinkProperties and tell our owner that they are 161 // now empty. Note that from the moment that the interface is removed, any further 162 // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd 163 // code that parses them will not be able to resolve the ifindex to an interface name. 164 clearLinkProperties(); 165 mCallback.update(getInterfaceLinkState()); 166 } 167 } 168 169 @Override onInterfaceLinkStateChanged(String iface, boolean state)170 public void onInterfaceLinkStateChanged(String iface, boolean state) { 171 if (mInterfaceName.equals(iface)) { 172 maybeLog("interfaceLinkStateChanged", iface + (state ? " up" : " down")); 173 setInterfaceLinkState(state); 174 } 175 } 176 177 @Override onInterfaceAddressUpdated(LinkAddress address, String iface)178 public void onInterfaceAddressUpdated(LinkAddress address, String iface) { 179 if (mInterfaceName.equals(iface)) { 180 maybeLog("addressUpdated", iface, address); 181 boolean changed; 182 synchronized (this) { 183 changed = mLinkProperties.addLinkAddress(address); 184 } 185 if (changed) { 186 mCallback.update(getInterfaceLinkState()); 187 } 188 } 189 } 190 191 @Override onInterfaceAddressRemoved(LinkAddress address, String iface)192 public void onInterfaceAddressRemoved(LinkAddress address, String iface) { 193 if (mInterfaceName.equals(iface)) { 194 maybeLog("addressRemoved", iface, address); 195 boolean changed; 196 synchronized (this) { 197 changed = mLinkProperties.removeLinkAddress(address); 198 } 199 if (changed) { 200 mCallback.update(getInterfaceLinkState()); 201 } 202 } 203 } 204 205 @Override onRouteUpdated(RouteInfo route)206 public void onRouteUpdated(RouteInfo route) { 207 if (mInterfaceName.equals(route.getInterface())) { 208 maybeLog("routeUpdated", route); 209 boolean changed; 210 synchronized (this) { 211 changed = mLinkProperties.addRoute(route); 212 } 213 if (changed) { 214 mCallback.update(getInterfaceLinkState()); 215 } 216 } 217 } 218 219 @Override onRouteRemoved(RouteInfo route)220 public void onRouteRemoved(RouteInfo route) { 221 if (mInterfaceName.equals(route.getInterface())) { 222 maybeLog("routeRemoved", route); 223 boolean changed; 224 synchronized (this) { 225 changed = mLinkProperties.removeRoute(route); 226 } 227 if (changed) { 228 mCallback.update(getInterfaceLinkState()); 229 } 230 } 231 } 232 233 @Override onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses)234 public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { 235 if (mInterfaceName.equals(iface)) { 236 maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses)); 237 boolean changed = mDnsServerRepository.addServers(lifetime, addresses); 238 if (changed) { 239 synchronized (this) { 240 mDnsServerRepository.setDnsServersOn(mLinkProperties); 241 } 242 mCallback.update(getInterfaceLinkState()); 243 } 244 } 245 } 246 247 /** 248 * Returns a copy of this object's LinkProperties. 249 */ getLinkProperties()250 public synchronized LinkProperties getLinkProperties() { 251 return new LinkProperties(mLinkProperties); 252 } 253 254 /** 255 * Reset this object's LinkProperties. 256 */ clearLinkProperties()257 public synchronized void clearLinkProperties() { 258 // Clear the repository before clearing mLinkProperties. That way, if a clear() happens 259 // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in 260 // mLinkProperties, as desired. 261 mDnsServerRepository = new DnsServerRepository(mConfig.minRdnssLifetime); 262 mNetlinkMonitor.clearAlarms(); 263 mLinkProperties.clear(); 264 mLinkProperties.setInterfaceName(mInterfaceName); 265 } 266 getInterfaceLinkState()267 private synchronized boolean getInterfaceLinkState() { 268 return mInterfaceLinkState; 269 } 270 setInterfaceLinkState(boolean state)271 private synchronized void setInterfaceLinkState(boolean state) { 272 mInterfaceLinkState = state; 273 } 274 275 /** Notifies this object of new interface parameters. */ setInterfaceParams(InterfaceParams params)276 public void setInterfaceParams(InterfaceParams params) { 277 mNetlinkMonitor.setIfindex(params.index); 278 } 279 280 /** Notifies this object not to listen on any interface. */ clearInterfaceParams()281 public void clearInterfaceParams() { 282 mNetlinkMonitor.setIfindex(0); // 0 is never a valid ifindex. 283 } 284 285 /** 286 * Simple NetlinkMonitor. Currently only listens for PREF64 events. 287 * All methods except the constructor must be called on the handler thread. 288 */ 289 private class MyNetlinkMonitor extends NetlinkMonitor { 290 private final Handler mHandler; 291 MyNetlinkMonitor(Handler h, SharedLog log, String tag)292 MyNetlinkMonitor(Handler h, SharedLog log, String tag) { 293 super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT); 294 mHandler = h; 295 } 296 297 private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); 298 299 private long mNat64PrefixExpiry; 300 301 /** 302 * Current interface index. Most of this class (and of IpClient), only uses interface names, 303 * not interface indices. This means that the interface index can in theory change, and that 304 * it's not necessarily correct to get the interface name at object creation time (and in 305 * fact, when the object is created, the interface might not even exist). 306 * TODO: once all netlink events pass through this class, stop depending on interface names. 307 */ 308 private int mIfindex; 309 setIfindex(int ifindex)310 void setIfindex(int ifindex) { 311 mIfindex = ifindex; 312 } 313 clearAlarms()314 void clearAlarms() { 315 cancelPref64Alarm(); 316 } 317 318 private final AlarmManager.OnAlarmListener mExpirePref64Alarm = () -> { 319 // Ignore the alarm if cancelPref64Alarm has already been called. 320 // 321 // TODO: in the rare case where the alarm fires and posts the lambda to the handler 322 // thread while we are processing an RA that changes the lifetime of the same prefix, 323 // this code will run anyway even if the alarm is rescheduled or cancelled. If the 324 // lifetime in the RA is zero this code will correctly do nothing, but if the lifetime 325 // is nonzero then the prefix will be added and immediately removed by this code. 326 if (mNat64PrefixExpiry == 0) return; 327 updatePref64(mShim.getNat64Prefix(mLinkProperties), 328 mNat64PrefixExpiry, mNat64PrefixExpiry); 329 }; 330 cancelPref64Alarm()331 private void cancelPref64Alarm() { 332 // Clear the expiry in case the alarm just fired and has not been processed yet. 333 if (mNat64PrefixExpiry == 0) return; 334 mNat64PrefixExpiry = 0; 335 mAlarmManager.cancel(mExpirePref64Alarm); 336 } 337 schedulePref64Alarm()338 private void schedulePref64Alarm() { 339 // There is no need to cancel any existing alarms, because we are using the same 340 // OnAlarmListener object, and each such listener can only have at most one alarm. 341 final String tag = mTag + ".PREF64"; 342 mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNat64PrefixExpiry, tag, 343 mExpirePref64Alarm, mHandler); 344 } 345 346 /** 347 * Processes a PREF64 ND option. 348 * 349 * @param prefix The NAT64 prefix. 350 * @param now The time (as determined by SystemClock.elapsedRealtime) when the event 351 * that triggered this method was received. 352 * @param expiry The time (as determined by SystemClock.elapsedRealtime) when the option 353 * expires. 354 */ updatePref64(IpPrefix prefix, final long now, final long expiry)355 private synchronized void updatePref64(IpPrefix prefix, final long now, 356 final long expiry) { 357 final IpPrefix currentPrefix = mShim.getNat64Prefix(mLinkProperties); 358 359 // If the prefix matches the current prefix, refresh its lifetime. 360 if (prefix.equals(currentPrefix)) { 361 mNat64PrefixExpiry = expiry; 362 if (expiry > now) { 363 schedulePref64Alarm(); 364 } 365 } 366 367 // If we already have a prefix, continue using it and ignore the new one. Stopping and 368 // restarting clatd is disruptive because it will break existing IPv4 connections. 369 // Note: this means that if we receive an RA that adds a new prefix and deletes the old 370 // prefix, we might receive and ignore the new prefix, then delete the old prefix, and 371 // have no prefix until the next RA is received. This is because the kernel returns ND 372 // user options one at a time even if they are in the same RA. 373 // TODO: keep track of the last few prefixes seen, like DnsServerRepository does. 374 if (mNat64PrefixExpiry > now) return; 375 376 // The current prefix has expired. Either replace it with the new one or delete it. 377 if (expiry > now) { 378 // If expiry > now, then prefix != currentPrefix (due to the return statement above) 379 mShim.setNat64Prefix(mLinkProperties, prefix); 380 mNat64PrefixExpiry = expiry; 381 schedulePref64Alarm(); 382 } else { 383 mShim.setNat64Prefix(mLinkProperties, null); 384 cancelPref64Alarm(); 385 } 386 387 mCallback.update(getInterfaceLinkState()); 388 } 389 processPref64Option(StructNdOptPref64 opt, final long now)390 private void processPref64Option(StructNdOptPref64 opt, final long now) { 391 final long expiry = now + TimeUnit.SECONDS.toMillis(opt.lifetime); 392 updatePref64(opt.prefix, now, expiry); 393 } 394 processNduseroptMessage(NduseroptMessage msg, final long whenMs)395 private void processNduseroptMessage(NduseroptMessage msg, final long whenMs) { 396 if (msg.family != AF_INET6 || msg.option == null || msg.ifindex != mIfindex) return; 397 if (msg.icmp_type != (byte) ICMPV6_ROUTER_ADVERTISEMENT) return; 398 399 switch (msg.option.type) { 400 case StructNdOptPref64.TYPE: 401 processPref64Option((StructNdOptPref64) msg.option, whenMs); 402 break; 403 404 default: 405 // TODO: implement RDNSS and DNSSL. 406 break; 407 } 408 } 409 410 @Override processNetlinkMessage(NetlinkMessage nlMsg, long whenMs)411 protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { 412 if (!(nlMsg instanceof NduseroptMessage)) return; 413 processNduseroptMessage((NduseroptMessage) nlMsg, whenMs); 414 } 415 } 416 417 /** 418 * Tracks DNS server updates received from Netlink. 419 * 420 * The network may announce an arbitrary number of DNS servers in Router Advertisements at any 421 * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be 422 * used any more. In this way, the network can gracefully migrate clients from one set of DNS 423 * servers to another. Announcements can both raise and lower the lifetime, and an announcement 424 * can expire servers by announcing them with a lifetime of zero. 425 * 426 * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of 427 * DNS servers at any given time. These are referred to as the current servers. In case all the 428 * current servers expire, the class also keeps track of a larger (but limited) number of 429 * servers that are promoted to current servers when the current ones expire. In order to 430 * minimize updates to the rest of the system (and potentially expensive cache flushes) this 431 * class attempts to keep the list of current servers constant where possible. More 432 * specifically, the list of current servers is only updated if a new server is learned and 433 * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the 434 * current servers expires or is pushed out of the set. Therefore, the current servers will not 435 * necessarily be the ones with the highest lifetime, but the ones learned first. 436 * 437 * This is by design: if instead the class always preferred the servers with the highest 438 * lifetime, a (misconfigured?) network where two or more routers announce more than 439 * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations. 440 * 441 * TODO: Currently servers are only expired when a new DNS update is received. 442 * Update them using timers, or possibly on every notification received by NetlinkTracker. 443 * 444 * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink 445 * notifications are sent by multiple threads. If future threads use alarms to expire, those 446 * alarms must also be synchronized(this). 447 * 448 */ 449 private static class DnsServerRepository { 450 451 /** How many DNS servers we will use. 3 is suggested by RFC 6106. */ 452 static final int NUM_CURRENT_SERVERS = 3; 453 454 /** How many DNS servers we'll keep track of, in total. */ 455 static final int NUM_SERVERS = 12; 456 457 /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */ 458 private Set<InetAddress> mCurrentServers; 459 460 public static final String TAG = "DnsServerRepository"; 461 462 /** 463 * Stores all the DNS servers we know about, for use when the current servers expire. 464 * Always sorted in order of decreasing expiry. The elements in this list are also the 465 * values of mIndex, and may be elements in mCurrentServers. 466 */ 467 private ArrayList<DnsServerEntry> mAllServers; 468 469 /** 470 * Indexes the servers so we can update their lifetimes more quickly in the common case 471 * where servers are not being added, but only being refreshed. 472 */ 473 private HashMap<InetAddress, DnsServerEntry> mIndex; 474 475 /** 476 * Minimum (non-zero) RDNSS lifetime to accept. 477 */ 478 private final int mMinLifetime; 479 DnsServerRepository(int minLifetime)480 DnsServerRepository(int minLifetime) { 481 mCurrentServers = new HashSet<>(); 482 mAllServers = new ArrayList<>(NUM_SERVERS); 483 mIndex = new HashMap<>(NUM_SERVERS); 484 mMinLifetime = minLifetime; 485 } 486 487 /** Sets the DNS servers of the provided LinkProperties object to the current servers. */ setDnsServersOn(LinkProperties lp)488 public synchronized void setDnsServersOn(LinkProperties lp) { 489 lp.setDnsServers(mCurrentServers); 490 } 491 492 /** 493 * Notifies the class of new DNS server information. 494 * @param lifetime the time in seconds that the DNS servers are valid. 495 * @param addresses the string representations of the IP addresses of DNS servers to use. 496 */ addServers(long lifetime, String[] addresses)497 public synchronized boolean addServers(long lifetime, String[] addresses) { 498 // If the servers are below the minimum lifetime, don't change anything. 499 if (lifetime != 0 && lifetime < mMinLifetime) return false; 500 501 // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned. 502 // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds 503 // (136 years) is close enough. 504 long now = System.currentTimeMillis(); 505 long expiry = now + 1000 * lifetime; 506 507 // Go through the list of servers. For each one, update the entry if one exists, and 508 // create one if it doesn't. 509 for (String addressString : addresses) { 510 InetAddress address; 511 try { 512 address = InetAddresses.parseNumericAddress(addressString); 513 } catch (IllegalArgumentException ex) { 514 continue; 515 } 516 517 if (!updateExistingEntry(address, expiry)) { 518 // There was no entry for this server. Create one, unless it's already expired 519 // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned). 520 if (expiry > now) { 521 DnsServerEntry entry = new DnsServerEntry(address, expiry); 522 mAllServers.add(entry); 523 mIndex.put(address, entry); 524 } 525 } 526 } 527 528 // Sort the servers by expiry. 529 Collections.sort(mAllServers); 530 531 // Prune excess entries and update the current server list. 532 return updateCurrentServers(); 533 } 534 updateExistingEntry(InetAddress address, long expiry)535 private synchronized boolean updateExistingEntry(InetAddress address, long expiry) { 536 DnsServerEntry existing = mIndex.get(address); 537 if (existing != null) { 538 existing.expiry = expiry; 539 return true; 540 } 541 return false; 542 } 543 updateCurrentServers()544 private synchronized boolean updateCurrentServers() { 545 long now = System.currentTimeMillis(); 546 boolean changed = false; 547 548 // Prune excess or expired entries. 549 for (int i = mAllServers.size() - 1; i >= 0; i--) { 550 if (i >= NUM_SERVERS || mAllServers.get(i).expiry <= now) { 551 DnsServerEntry removed = mAllServers.remove(i); 552 mIndex.remove(removed.address); 553 changed |= mCurrentServers.remove(removed.address); 554 } else { 555 break; 556 } 557 } 558 559 // Add servers to the current set, in order of decreasing lifetime, until it has enough. 560 // Prefer existing servers over new servers in order to minimize updates to the rest of 561 // the system and avoid persistent oscillations. 562 for (DnsServerEntry entry : mAllServers) { 563 if (mCurrentServers.size() < NUM_CURRENT_SERVERS) { 564 changed |= mCurrentServers.add(entry.address); 565 } else { 566 break; 567 } 568 } 569 return changed; 570 } 571 } 572 573 /** 574 * Represents a DNS server entry with an expiry time. 575 * 576 * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first. 577 * The ordering of entries with the same lifetime is unspecified, because given two servers with 578 * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much 579 * faster than comparing the IP address as well. 580 * 581 * Note: this class has a natural ordering that is inconsistent with equals. 582 */ 583 private static class DnsServerEntry implements Comparable<DnsServerEntry> { 584 /** The IP address of the DNS server. */ 585 public final InetAddress address; 586 /** The time until which the DNS server may be used. A Java millisecond time as might be 587 * returned by currentTimeMillis(). */ 588 public long expiry; 589 DnsServerEntry(InetAddress address, long expiry)590 DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException { 591 this.address = address; 592 this.expiry = expiry; 593 } 594 compareTo(DnsServerEntry other)595 public int compareTo(DnsServerEntry other) { 596 return Long.compare(other.expiry, this.expiry); 597 } 598 } 599 } 600