diff options
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; } |