PageRenderTime 455ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/core/java/com/android/server/net/NetlinkTracker.java

https://gitlab.com/AvayKumar/android_frameworks_base
Java | 365 lines | 194 code | 39 blank | 132 comment | 24 complexity | 4f20e5c5d20fa5d5b96b215ed2202539 MD5 | raw file
  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. package com.android.server.net;
  17. import android.net.LinkAddress;
  18. import android.net.LinkProperties;
  19. import android.net.RouteInfo;
  20. import android.util.Log;
  21. import java.net.InetAddress;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collection;
  25. import java.util.Collections;
  26. import java.util.HashMap;
  27. import java.util.HashSet;
  28. import java.util.Objects;
  29. import java.util.Set;
  30. /**
  31. * Keeps track of link configuration received from Netlink.
  32. *
  33. * Instances of this class are expected to be owned by subsystems such as Wi-Fi
  34. * or Ethernet that manage one or more network interfaces. Each interface to be
  35. * tracked needs its own {@code NetlinkTracker}.
  36. *
  37. * An instance of this class is constructed by passing in an interface name and
  38. * a callback. The owner is then responsible for registering the tracker with
  39. * NetworkManagementService. When the class receives update notifications from
  40. * the NetworkManagementService notification threads, it applies the update to
  41. * its local LinkProperties, and if something has changed, notifies its owner of
  42. * the update via the callback.
  43. *
  44. * The owner can then call {@code getLinkProperties()} in order to find out
  45. * what changed. If in the meantime the LinkProperties stored here have changed,
  46. * this class will return the current LinkProperties. Because each change
  47. * triggers an update callback after the change is made, the owner may get more
  48. * callbacks than strictly necessary (some of which may be no-ops), but will not
  49. * be out of sync once all callbacks have been processed.
  50. *
  51. * Threading model:
  52. *
  53. * - The owner of this class is expected to create it, register it, and call
  54. * getLinkProperties or clearLinkProperties on its thread.
  55. * - Most of the methods in the class are inherited from BaseNetworkObserver
  56. * and are called by NetworkManagementService notification threads.
  57. * - All accesses to mLinkProperties must be synchronized(this). All the other
  58. * member variables are immutable once the object is constructed.
  59. *
  60. * This class currently tracks IPv4 and IPv6 addresses. In the future it will
  61. * track routes and DNS servers.
  62. *
  63. * @hide
  64. */
  65. public class NetlinkTracker extends BaseNetworkObserver {
  66. private final String TAG;
  67. public interface Callback {
  68. public void update(LinkProperties lp);
  69. }
  70. private final String mInterfaceName;
  71. private final Callback mCallback;
  72. private final LinkProperties mLinkProperties;
  73. private DnsServerRepository mDnsServerRepository;
  74. private static final boolean DBG = false;
  75. public NetlinkTracker(String iface, Callback callback) {
  76. TAG = "NetlinkTracker/" + iface;
  77. mInterfaceName = iface;
  78. mCallback = callback;
  79. mLinkProperties = new LinkProperties();
  80. mLinkProperties.setInterfaceName(mInterfaceName);
  81. mDnsServerRepository = new DnsServerRepository();
  82. }
  83. private void maybeLog(String operation, String iface, LinkAddress address) {
  84. if (DBG) {
  85. Log.d(TAG, operation + ": " + address + " on " + iface +
  86. " flags " + address.getFlags() + " scope " + address.getScope());
  87. }
  88. }
  89. private void maybeLog(String operation, Object o) {
  90. if (DBG) {
  91. Log.d(TAG, operation + ": " + o.toString());
  92. }
  93. }
  94. @Override
  95. public void addressUpdated(String iface, LinkAddress address) {
  96. if (mInterfaceName.equals(iface)) {
  97. maybeLog("addressUpdated", iface, address);
  98. boolean changed;
  99. synchronized (this) {
  100. changed = mLinkProperties.addLinkAddress(address);
  101. }
  102. if (changed) {
  103. mCallback.update(new LinkProperties(mLinkProperties));
  104. }
  105. }
  106. }
  107. @Override
  108. public void addressRemoved(String iface, LinkAddress address) {
  109. if (mInterfaceName.equals(iface)) {
  110. maybeLog("addressRemoved", iface, address);
  111. boolean changed;
  112. synchronized (this) {
  113. changed = mLinkProperties.removeLinkAddress(address);
  114. }
  115. if (changed) {
  116. mCallback.update(new LinkProperties(mLinkProperties));
  117. }
  118. }
  119. }
  120. @Override
  121. public void routeUpdated(RouteInfo route) {
  122. if (mInterfaceName.equals(route.getInterface())) {
  123. maybeLog("routeUpdated", route);
  124. boolean changed;
  125. synchronized (this) {
  126. changed = mLinkProperties.addRoute(route);
  127. }
  128. if (changed) {
  129. mCallback.update(new LinkProperties(mLinkProperties));
  130. }
  131. }
  132. }
  133. @Override
  134. public void routeRemoved(RouteInfo route) {
  135. if (mInterfaceName.equals(route.getInterface())) {
  136. maybeLog("routeRemoved", route);
  137. boolean changed;
  138. synchronized (this) {
  139. changed = mLinkProperties.removeRoute(route);
  140. }
  141. if (changed) {
  142. mCallback.update(new LinkProperties(mLinkProperties));
  143. }
  144. }
  145. }
  146. @Override
  147. public void interfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
  148. if (mInterfaceName.equals(iface)) {
  149. maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses));
  150. boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
  151. if (changed) {
  152. synchronized (this) {
  153. mDnsServerRepository.setDnsServersOn(mLinkProperties);
  154. }
  155. mCallback.update(new LinkProperties(mLinkProperties));
  156. }
  157. }
  158. }
  159. /**
  160. * Returns a copy of this object's LinkProperties.
  161. */
  162. public synchronized LinkProperties getLinkProperties() {
  163. return new LinkProperties(mLinkProperties);
  164. }
  165. public synchronized void clearLinkProperties() {
  166. // Clear the repository before clearing mLinkProperties. That way, if a clear() happens
  167. // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in
  168. // mLinkProperties, as desired.
  169. mDnsServerRepository = new DnsServerRepository();
  170. mLinkProperties.clear();
  171. mLinkProperties.setInterfaceName(mInterfaceName);
  172. }
  173. }
  174. /**
  175. * Represents a DNS server entry with an expiry time.
  176. *
  177. * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first.
  178. * The ordering of entries with the same lifetime is unspecified, because given two servers with
  179. * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much
  180. * faster than comparing the IP address as well.
  181. *
  182. * Note: this class has a natural ordering that is inconsistent with equals.
  183. */
  184. class DnsServerEntry implements Comparable<DnsServerEntry> {
  185. /** The IP address of the DNS server. */
  186. public final InetAddress address;
  187. /** The time until which the DNS server may be used. A Java millisecond time as might be
  188. * returned by currentTimeMillis(). */
  189. public long expiry;
  190. public DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException {
  191. this.address = address;
  192. this.expiry = expiry;
  193. }
  194. public int compareTo(DnsServerEntry other) {
  195. return Long.compare(other.expiry, this.expiry);
  196. }
  197. }
  198. /**
  199. * Tracks DNS server updates received from Netlink.
  200. *
  201. * The network may announce an arbitrary number of DNS servers in Router Advertisements at any
  202. * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be used
  203. * any more. In this way, the network can gracefully migrate clients from one set of DNS servers to
  204. * another. Announcements can both raise and lower the lifetime, and an announcement can expire
  205. * servers by announcing them with a lifetime of zero.
  206. *
  207. * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of DNS
  208. * servers at any given time. These are referred to as the current servers. In case all the
  209. * current servers expire, the class also keeps track of a larger (but limited) number of servers
  210. * that are promoted to current servers when the current ones expire. In order to minimize updates
  211. * to the rest of the system (and potentially expensive cache flushes) this class attempts to keep
  212. * the list of current servers constant where possible. More specifically, the list of current
  213. * servers is only updated if a new server is learned and there are not yet {@code
  214. * NUM_CURRENT_SERVERS} current servers, or if one or more of the current servers expires or is
  215. * pushed out of the set. Therefore, the current servers will not necessarily be the ones with the
  216. * highest lifetime, but the ones learned first.
  217. *
  218. * This is by design: if instead the class always preferred the servers with the highest lifetime, a
  219. * (misconfigured?) network where two or more routers announce more than {@code NUM_CURRENT_SERVERS}
  220. * unique servers would cause persistent oscillations.
  221. *
  222. * TODO: Currently servers are only expired when a new DNS update is received.
  223. * Update them using timers, or possibly on every notification received by NetlinkTracker.
  224. *
  225. * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink
  226. * notifications are sent by multiple threads. If future threads use alarms to expire, those
  227. * alarms must also be synchronized(this).
  228. *
  229. */
  230. class DnsServerRepository {
  231. /** How many DNS servers we will use. 3 is suggested by RFC 6106. */
  232. public static final int NUM_CURRENT_SERVERS = 3;
  233. /** How many DNS servers we'll keep track of, in total. */
  234. public static final int NUM_SERVERS = 12;
  235. /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */
  236. private Set<InetAddress> mCurrentServers;
  237. public static final String TAG = "DnsServerRepository";
  238. /**
  239. * Stores all the DNS servers we know about, for use when the current servers expire.
  240. * Always sorted in order of decreasing expiry. The elements in this list are also the values
  241. * of mIndex, and may be elements in mCurrentServers.
  242. */
  243. private ArrayList<DnsServerEntry> mAllServers;
  244. /**
  245. * Indexes the servers so we can update their lifetimes more quickly in the common case where
  246. * servers are not being added, but only being refreshed.
  247. */
  248. private HashMap<InetAddress, DnsServerEntry> mIndex;
  249. public DnsServerRepository() {
  250. mCurrentServers = new HashSet();
  251. mAllServers = new ArrayList<DnsServerEntry>(NUM_SERVERS);
  252. mIndex = new HashMap<InetAddress, DnsServerEntry>(NUM_SERVERS);
  253. }
  254. /** Sets the DNS servers of the provided LinkProperties object to the current servers. */
  255. public synchronized void setDnsServersOn(LinkProperties lp) {
  256. lp.setDnsServers(mCurrentServers);
  257. }
  258. /**
  259. * Notifies the class of new DNS server information.
  260. * @param lifetime the time in seconds that the DNS servers are valid.
  261. * @param addresses the string representations of the IP addresses of the DNS servers to use.
  262. */
  263. public synchronized boolean addServers(long lifetime, String[] addresses) {
  264. // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned.
  265. // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds
  266. // (136 years) is close enough.
  267. long now = System.currentTimeMillis();
  268. long expiry = now + 1000 * lifetime;
  269. // Go through the list of servers. For each one, update the entry if one exists, and
  270. // create one if it doesn't.
  271. for (String addressString : addresses) {
  272. InetAddress address;
  273. try {
  274. address = InetAddress.parseNumericAddress(addressString);
  275. } catch (IllegalArgumentException ex) {
  276. continue;
  277. }
  278. if (!updateExistingEntry(address, expiry)) {
  279. // There was no entry for this server. Create one, unless it's already expired
  280. // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned).
  281. if (expiry > now) {
  282. DnsServerEntry entry = new DnsServerEntry(address, expiry);
  283. mAllServers.add(entry);
  284. mIndex.put(address, entry);
  285. }
  286. }
  287. }
  288. // Sort the servers by expiry.
  289. Collections.sort(mAllServers);
  290. // Prune excess entries and update the current server list.
  291. return updateCurrentServers();
  292. }
  293. private synchronized boolean updateExistingEntry(InetAddress address, long expiry) {
  294. DnsServerEntry existing = mIndex.get(address);
  295. if (existing != null) {
  296. existing.expiry = expiry;
  297. return true;
  298. }
  299. return false;
  300. }
  301. private synchronized boolean updateCurrentServers() {
  302. long now = System.currentTimeMillis();
  303. boolean changed = false;
  304. // Prune excess or expired entries.
  305. for (int i = mAllServers.size() - 1; i >= 0; i--) {
  306. if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) {
  307. DnsServerEntry removed = mAllServers.remove(i);
  308. mIndex.remove(removed.address);
  309. changed |= mCurrentServers.remove(removed.address);
  310. } else {
  311. break;
  312. }
  313. }
  314. // Add servers to the current set, in order of decreasing lifetime, until it has enough.
  315. // Prefer existing servers over new servers in order to minimize updates to the rest of the
  316. // system and avoid persistent oscillations.
  317. for (DnsServerEntry entry : mAllServers) {
  318. if (mCurrentServers.size() < NUM_CURRENT_SERVERS) {
  319. changed |= mCurrentServers.add(entry.address);
  320. } else {
  321. break;
  322. }
  323. }
  324. return changed;
  325. }
  326. }