summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java10
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java18
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java208
-rw-r--r--services/net/java/android/net/ip/RouterAdvertisementDaemon.java234
4 files changed, 320 insertions, 150 deletions
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index b6c8d5d87b4c..e0d8373fc445 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1392,6 +1392,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
for (Integer netType : mUpstreamIfaceTypes) {
NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+ // TODO: if the network is suspended we should consider
+ // that to be the same as connected here.
if ((info != null) && info.isConnected()) {
upType = netType.intValue();
break;
@@ -1465,6 +1467,10 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// it immediately, because there likely will be no second
// EVENT_ON_AVAILABLE (it was already received).
handleNewUpstreamNetworkState(ns);
+ } else if (mCurrentUpstreamIface == null) {
+ // There are no available upstream networks, or none that
+ // have an IPv4 default route (current metric for success).
+ handleNewUpstreamNetworkState(null);
}
}
@@ -1639,6 +1645,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
chooseUpstreamType(mTryCell);
mTryCell = !mTryCell;
}
+
@Override
public void exit() {
// TODO: examine if we should check the return value.
@@ -1646,7 +1653,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
mUpstreamNetworkMonitor.stop();
stopListeningForSimChanges();
notifyTetheredOfNewUpstreamIface(null);
+ handleNewUpstreamNetworkState(null);
}
+
@Override
public boolean processMessage(Message message) {
maybeLogMessage(this, message.what);
@@ -1734,6 +1743,7 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering
// reevaluation is triggered via received CONNECTIVITY_ACTION
// broadcasts that result in being passed a
// TetherMasterSM.CMD_UPSTREAM_CHANGED.
+ handleNewUpstreamNetworkState(null);
break;
default:
break;
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
index 825439786360..e94b584ac48a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java
@@ -55,7 +55,7 @@ public class IPv6TetheringCoordinator {
if (VDBG) {
Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
}
- if (ns == null || ns.network == null) {
+ if (!canTetherIPv6(ns)) {
stopIPv6TetheringOnAllInterfaces();
setUpstreamNetworkState(null);
return;
@@ -65,8 +65,9 @@ public class IPv6TetheringCoordinator {
!ns.network.equals(mUpstreamNetworkState.network)) {
stopIPv6TetheringOnAllInterfaces();
}
+
setUpstreamNetworkState(ns);
- maybeUpdateIPv6TetheringInterfaces();
+ updateIPv6TetheringInterfaces();
}
private void stopIPv6TetheringOnAllInterfaces() {
@@ -77,9 +78,10 @@ public class IPv6TetheringCoordinator {
}
private void setUpstreamNetworkState(NetworkState ns) {
- if (!canTetherIPv6(ns)) {
+ if (ns == null) {
mUpstreamNetworkState = null;
} else {
+ // Make a deep copy of the parts we need.
mUpstreamNetworkState = new NetworkState(
null,
new LinkProperties(ns.linkProperties),
@@ -94,19 +96,17 @@ public class IPv6TetheringCoordinator {
}
}
- private void maybeUpdateIPv6TetheringInterfaces() {
- if (mUpstreamNetworkState == null) return;
-
+ private void updateIPv6TetheringInterfaces() {
for (TetherInterfaceStateMachine sm : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(sm.interfaceType());
- if (lp != null) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
- }
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
break;
}
}
private LinkProperties getInterfaceIPv6LinkProperties(int interfaceType) {
+ if (mUpstreamNetworkState == null) return null;
+
// NOTE: Here, in future, we would have policies to decide how to divvy
// up the available dedicated prefixes among downstream interfaces.
// At this time we have no such mechanism--we only support tethering
diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
index b47e079395d8..d087f903560a 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity.tethering;
+import android.net.INetd;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
@@ -27,13 +28,16 @@ import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Slog;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Objects;
/**
@@ -41,13 +45,15 @@ import java.util.HashSet;
*/
class IPv6TetheringInterfaceServices {
private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
+ private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
+ private static final int RFC7421_IP_PREFIX_LENGTH = 64;
private final String mIfName;
private final INetworkManagementService mNMService;
private NetworkInterface mNetworkInterface;
private byte[] mHwAddr;
- private ArrayList<RouteInfo> mLastLocalRoutes;
+ private LinkProperties mLastIPv6LinkProperties;
private RouterAdvertisementDaemon mRaDaemon;
private RaParams mLastRaParams;
@@ -86,8 +92,7 @@ class IPv6TetheringInterfaceServices {
public void stop() {
mNetworkInterface = null;
mHwAddr = null;
- updateLocalRoutes(null);
- updateRaParams(null);
+ setRaParams(null);
if (mRaDaemon != null) {
mRaDaemon.stop();
@@ -104,95 +109,178 @@ class IPv6TetheringInterfaceServices {
public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
if (mRaDaemon == null) return;
- if (v6only == null) {
- updateLocalRoutes(null);
- updateRaParams(null);
+ // Avoid unnecessary work on spurious updates.
+ if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
return;
}
- RaParams params = new RaParams();
- params.mtu = v6only.getMtu();
- params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
+ RaParams params = null;
- ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
- for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
- final IpPrefix prefix = new IpPrefix(linkAddr.getAddress(),
- linkAddr.getPrefixLength());
+ if (v6only != null) {
+ params = new RaParams();
+ params.mtu = v6only.getMtu();
+ params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
- // Accumulate routes representing "prefixes to be assigned to the
- // local interface", for subsequent addition to the local network
- // in the routing rules.
- localRoutes.add(new RouteInfo(prefix, null, mIfName));
+ for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
+ if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
- params.prefixes.add(prefix);
+ final IpPrefix prefix = new IpPrefix(
+ linkAddr.getAddress(), linkAddr.getPrefixLength());
+ params.prefixes.add(prefix);
+
+ final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
+ if (dnsServer != null) {
+ params.dnses.add(dnsServer);
+ }
+ }
}
+ // If v6only is null, we pass in null to setRaParams(), which handles
+ // deprecation of any existing RA data.
+
+ setRaParams(params);
+ mLastIPv6LinkProperties = v6only;
+ }
- // We need to be able to send unicast RAs, and clients might like to
- // ping the default router's link-local address, so add that as well.
- localRoutes.add(new RouteInfo(new IpPrefix("fe80::/64"), null, mIfName));
- // TODO: Add a local interface address, update dnsmasq to listen on the
- // new address, and use only that address as a DNS server.
- for (InetAddress dnsServer : v6only.getDnsServers()) {
- if (dnsServer instanceof Inet6Address) {
- params.dnses.add((Inet6Address) dnsServer);
+ private void configureLocalRoutes(
+ HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
+ // [1] Remove the routes that are deprecated.
+ if (!deprecatedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
+ try {
+ final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
+ if (removalFailures > 0) {
+ Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
+ removalFailures));
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
}
}
- updateLocalRoutes(localRoutes);
- updateRaParams(params);
- }
+ // [2] Add only the routes that have not previously been added.
+ if (newPrefixes != null && !newPrefixes.isEmpty()) {
+ HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
+ if (mLastRaParams != null) {
+ addedPrefixes.removeAll(mLastRaParams.prefixes);
+ }
- private void updateLocalRoutes(ArrayList<RouteInfo> localRoutes) {
- if (localRoutes != null) {
- // TODO: Compare with mLastLocalRoutes and take appropriate
- // appropriate action on the difference between the two.
+ if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
+ // We need to be able to send unicast RAs, and clients might
+ // like to ping the default router's link-local address. Note
+ // that we never remove the link-local route from the network
+ // until Tethering disables tethering on the interface.
+ addedPrefixes.add(LINK_LOCAL_PREFIX);
+ }
- if (!localRoutes.isEmpty()) {
+ if (!addedPrefixes.isEmpty()) {
+ final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
try {
- mNMService.addInterfaceToLocalNetwork(mIfName, localRoutes);
+ // It's safe to call addInterfaceToLocalNetwork() even if
+ // the interface is already in the local_network.
+ mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
} catch (RemoteException e) {
Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
}
}
- } else {
- if (mLastLocalRoutes != null && !mLastLocalRoutes.isEmpty()) {
+ }
+ }
+
+ private void configureLocalDns(
+ HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
+ INetd netd = getNetdServiceOrNull();
+ if (netd == null) {
+ if (newDnses != null) newDnses.clear();
+ Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
+ return;
+ }
+
+ // [1] Remove deprecated local DNS IP addresses.
+ if (!deprecatedDnses.isEmpty()) {
+ for (Inet6Address dns : deprecatedDnses) {
+ final String dnsString = dns.getHostAddress();
try {
- final int removalFailures =
- mNMService.removeRoutesFromLocalNetwork(mLastLocalRoutes);
- if (removalFailures > 0) {
- Log.e(TAG,
- String.format("Failed to remove %d IPv6 routes from local table.",
- removalFailures));
- }
+ netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
} catch (RemoteException e) {
- Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
+ Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
}
}
}
- mLastLocalRoutes = localRoutes;
+ // [2] Add only the local DNS IP addresses that have not previously been added.
+ if (newDnses != null && !newDnses.isEmpty()) {
+ final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
+ if (mLastRaParams != null) {
+ addedDnses.removeAll(mLastRaParams.dnses);
+ }
+
+ for (Inet6Address dns : addedDnses) {
+ final String dnsString = dns.getHostAddress();
+ try {
+ netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
+ newDnses.remove(dns);
+ }
+ }
+ }
+
+ try {
+ netd.tetherApplyDnsInterfaces();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to update local DNS caching server");
+ if (newDnses != null) newDnses.clear();
+ }
}
- private void updateRaParams(RaParams params) {
+ private void setRaParams(RaParams newParams) {
if (mRaDaemon != null) {
- HashSet<IpPrefix> deprecated = null;
+ final RaParams deprecatedParams =
+ RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
- if (mLastRaParams != null) {
- deprecated = new HashSet<>();
+ configureLocalRoutes(deprecatedParams.prefixes,
+ (newParams != null) ? newParams.prefixes : null);
- for (IpPrefix ipp : mLastRaParams.prefixes) {
- if (params == null || !params.prefixes.contains(ipp)) {
- deprecated.add(ipp);
- }
- }
- }
+ configureLocalDns(deprecatedParams.dnses,
+ (newParams != null) ? newParams.dnses : null);
+
+ mRaDaemon.buildNewRa(deprecatedParams, newParams);
+ }
+
+ mLastRaParams = newParams;
+ }
+
+ // Accumulate routes representing "prefixes to be assigned to the local
+ // interface", for subsequent modification of local_network routing.
+ private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
+ final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
+ for (IpPrefix ipp : prefixes) {
+ localRoutes.add(new RouteInfo(ipp, null, mIfName));
+ }
+ return localRoutes;
+ }
- // Currently, we send spurious RAs (5) whenever there's any update.
- // TODO: Compare params with mLastParams to avoid spurious updates.
- mRaDaemon.buildNewRa(params, deprecated);
+ private INetd getNetdServiceOrNull() {
+ if (mNMService != null) {
+ try {
+ return mNMService.getNetdService();
+ } catch (RemoteException ignored) {
+ // This blocks until netd can be reached, but it can return
+ // null during a netd crash.
+ }
}
+ return null;
+ }
- mLastRaParams = params;
+ // Given a prefix like 2001:db8::/64 return 2001:db8::1.
+ private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
+ final byte[] dnsBytes = localPrefix.getRawAddress();
+ dnsBytes[dnsBytes.length - 1] = 0x1;
+ try {
+ return Inet6Address.getByAddress(null, dnsBytes, 0);
+ } catch (UnknownHostException e) {
+ Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
+ return null;
+ }
}
}
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 1a9d2f23935e..a52cdcc98b9c 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -47,6 +47,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -61,10 +62,6 @@ import java.util.concurrent.atomic.AtomicInteger;
* - Rewrite using Handler (and friends) so that AlarmManager can deliver
* "kick" messages when it's time to send a multicast RA.
*
- * - Support transmitting MAX_URGENT_RTR_ADVERTISEMENTS number of empty
- * RAs with zero default router lifetime when transitioning from an
- * advertising state to a non-advertising state.
- *
* @hide
*/
public class RouterAdvertisementDaemon {
@@ -112,8 +109,7 @@ public class RouterAdvertisementDaemon {
@GuardedBy("mLock")
private int mRaLength;
@GuardedBy("mLock")
- private final HashMap<IpPrefix, Integer> mDeprecatedPrefixes;
-
+ private final DeprecatedInfoTracker mDeprecatedInfoTracker;
@GuardedBy("mLock")
private RaParams mRaParams;
@@ -140,6 +136,88 @@ public class RouterAdvertisementDaemon {
prefixes = (HashSet) other.prefixes.clone();
dnses = (HashSet) other.dnses.clone();
}
+
+ // Returns the subset of RA parameters that become deprecated when
+ // moving from announcing oldRa to announcing newRa.
+ //
+ // Currently only tracks differences in |prefixes| and |dnses|.
+ public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
+ RaParams newlyDeprecated = new RaParams();
+
+ if (oldRa != null) {
+ for (IpPrefix ipp : oldRa.prefixes) {
+ if (newRa == null || !newRa.prefixes.contains(ipp)) {
+ newlyDeprecated.prefixes.add(ipp);
+ }
+ }
+
+ for (Inet6Address dns : oldRa.dnses) {
+ if (newRa == null || !newRa.dnses.contains(dns)) {
+ newlyDeprecated.dnses.add(dns);
+ }
+ }
+ }
+
+ return newlyDeprecated;
+ }
+ }
+
+ private static class DeprecatedInfoTracker {
+ private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
+ private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
+
+ Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
+
+ void putPrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removePrefixes(Set<IpPrefix> prefixes) {
+ for (IpPrefix ipp : prefixes) {
+ mPrefixes.remove(ipp);
+ }
+ }
+
+ Set<Inet6Address> getDnses() { return mDnses.keySet(); }
+
+ void putDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
+ }
+ }
+
+ void removeDnses(Set<Inet6Address> dnses) {
+ for (Inet6Address dns : dnses) {
+ mDnses.remove(dns);
+ }
+ }
+
+ boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
+
+ private boolean decrementCounters() {
+ boolean removed = decrementCounter(mPrefixes);
+ removed |= decrementCounter(mDnses);
+ return removed;
+ }
+
+ private <T> boolean decrementCounter(HashMap<T, Integer> map) {
+ boolean removed = false;
+
+ for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
+ it.hasNext();) {
+ Map.Entry<T, Integer> kv = it.next();
+ if (kv.getValue() == 0) {
+ it.remove();
+ removed = true;
+ } else {
+ kv.setValue(kv.getValue() - 1);
+ }
+ }
+
+ return removed;
+ }
}
@@ -148,29 +226,24 @@ public class RouterAdvertisementDaemon {
mIfIndex = ifindex;
mHwAddr = hwaddr;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
- mDeprecatedPrefixes = new HashMap<>();
+ mDeprecatedInfoTracker = new DeprecatedInfoTracker();
}
- public void buildNewRa(RaParams params, HashSet<IpPrefix> newlyDeprecated) {
- if (newlyDeprecated != null) {
- synchronized (mLock) {
- for (IpPrefix ipp : newlyDeprecated) {
- mDeprecatedPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
- }
+ public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
+ synchronized (mLock) {
+ if (deprecatedParams != null) {
+ mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
+ mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
}
- }
- // TODO: Send MAX_URGENT_RTR_ADVERTISEMENTS zero router lifetime RAs,
- // iff. we have already sent an RA.
- if (params == null || params.prefixes.isEmpty()) {
- // No RA to be served at this time.
- clearRa();
- return;
- }
+ if (newParams != null) {
+ // Process information that is no longer deprecated.
+ mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
+ mDeprecatedInfoTracker.removeDnses(newParams.dnses);
+ }
- synchronized (mLock) {
- mRaParams = params;
- assembleRa();
+ mRaParams = newParams;
+ assembleRaLocked();
}
maybeNotifyMulticastTransmitter();
@@ -196,73 +269,64 @@ public class RouterAdvertisementDaemon {
mUnicastResponder = null;
}
- private void clearRa() {
- boolean notifySocket;
- synchronized (mLock) {
- notifySocket = (mRaLength != 0);
- mRaLength = 0;
- }
- if (notifySocket) {
- maybeNotifyMulticastTransmitter();
- }
- }
-
- private void assembleRa() {
+ private void assembleRaLocked() {
final ByteBuffer ra = ByteBuffer.wrap(mRA);
ra.order(ByteOrder.BIG_ENDIAN);
- synchronized (mLock) {
- try {
- putHeader(ra, mRaParams.hasDefaultRoute);
-
- putSlla(ra, mHwAddr);
-
- // https://tools.ietf.org/html/rfc5175#section-4 says:
- //
- // "MUST NOT be added to a Router Advertisement message
- // if no flags in the option are set."
- //
- // putExpandedFlagsOption(ra);
+ boolean shouldSendRA = false;
+ try {
+ putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
+ putSlla(ra, mHwAddr);
+ mRaLength = ra.position();
+
+ // https://tools.ietf.org/html/rfc5175#section-4 says:
+ //
+ // "MUST NOT be added to a Router Advertisement message
+ // if no flags in the option are set."
+ //
+ // putExpandedFlagsOption(ra);
+
+ if (mRaParams != null) {
putMtu(ra, mRaParams.mtu);
+ mRaLength = ra.position();
for (IpPrefix ipp : mRaParams.prefixes) {
putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
- mDeprecatedPrefixes.remove(ipp);
- }
-
- for (IpPrefix ipp : mDeprecatedPrefixes.keySet()) {
- putPio(ra, ipp, 0, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
if (mRaParams.dnses.size() > 0) {
- putRdnss(ra, mRaParams.dnses);
+ putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
+ }
+ for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
+ putPio(ra, ipp, 0, 0);
mRaLength = ra.position();
- } catch (BufferOverflowException e) {
- Log.e(TAG, "Could not construct new RA: " + e);
- mRaLength = 0;
- return;
+ shouldSendRA = true;
}
- }
- }
- private int decrementDeprecatedPrefixes() {
- int removed = 0;
-
- synchronized (mLock) {
- for (Map.Entry<IpPrefix, Integer> kv : mDeprecatedPrefixes.entrySet()) {
- if (kv.getValue() == 0) {
- mDeprecatedPrefixes.remove(kv.getKey());
- removed++;
- } else {
- kv.setValue(kv.getValue() - 1);
- }
+ final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
+ if (!deprecatedDnses.isEmpty()) {
+ putRdnss(ra, deprecatedDnses, 0);
+ mRaLength = ra.position();
+ shouldSendRA = true;
}
+ } catch (BufferOverflowException e) {
+ // The packet up to mRaLength is valid, since it has been updated
+ // progressively as the RA was built. Log an error, and continue
+ // on as best as possible.
+ Log.e(TAG, "Could not construct new RA: " + e);
}
- return removed;
+ // We have nothing worth announcing; indicate as much to maybeSendRA().
+ if (!shouldSendRA) {
+ mRaLength = 0;
+ }
}
private void maybeNotifyMulticastTransmitter() {
@@ -461,7 +525,7 @@ public class RouterAdvertisementDaemon {
}
}
- private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses) {
+ private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
/**
Recursive DNS Server (RDNSS) Option
@@ -483,9 +547,15 @@ public class RouterAdvertisementDaemon {
ra.put(ND_OPTION_RDNSS)
.put(RDNSS_NUM_8OCTETS)
.putShort(asShort(0))
- .putInt(DEFAULT_LIFETIME);
+ .putInt(lifetime);
for (Inet6Address dns : dnses) {
+ // NOTE: If the full of list DNS servers doesn't fit in the packet,
+ // this code will cause a buffer overflow and the RA won't include
+ // include this instance of the option at all.
+ //
+ // TODO: Consider looking at ra.remaining() to determine how many
+ // DNS servers will fit, and adding only those.
ra.put(dns.getAddress());
}
}
@@ -601,10 +671,12 @@ public class RouterAdvertisementDaemon {
}
maybeSendRA(mAllNodes);
- if (decrementDeprecatedPrefixes() > 0) {
- // At least one deprecated PIO has been removed;
- // reassemble the RA.
- assembleRa();
+ synchronized (mLock) {
+ if (mDeprecatedInfoTracker.decrementCounters()) {
+ // At least one deprecated PIO has been removed;
+ // reassemble the RA.
+ assembleRaLocked();
+ }
}
}
}
@@ -619,17 +691,17 @@ public class RouterAdvertisementDaemon {
}
private int getNextMulticastTransmitDelaySec() {
- int countDeprecatedPrefixes = 0;
+ boolean deprecationInProgress = false;
synchronized (mLock) {
if (mRaLength < MIN_RA_HEADER_SIZE) {
// No actual RA to send; just sleep for 1 day.
return DAY_IN_SECONDS;
}
- countDeprecatedPrefixes = mDeprecatedPrefixes.size();
+ deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
}
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if (urgentPending > 0 || countDeprecatedPrefixes > 0) {
+ if ((urgentPending > 0) || deprecationInProgress) {
return MIN_DELAY_BETWEEN_RAS_SEC;
}