Merge "Handle setting and deprecating local DNS IPv6 addresses" into nyc-mr1-dev
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
// 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));
|
||||
setRaParams(params);
|
||||
mLastIPv6LinkProperties = v6only;
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
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));
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mLastLocalRoutes = localRoutes;
|
||||
}
|
||||
|
||||
private void updateRaParams(RaParams params) {
|
||||
if (mRaDaemon != null) {
|
||||
HashSet<IpPrefix> deprecated = null;
|
||||
|
||||
if (mLastRaParams != null) {
|
||||
deprecated = new HashSet<>();
|
||||
|
||||
for (IpPrefix ipp : mLastRaParams.prefixes) {
|
||||
if (params == null || !params.prefixes.contains(ipp)) {
|
||||
deprecated.add(ipp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
mLastRaParams = params;
|
||||
// [1] Remove deprecated local DNS IP addresses.
|
||||
if (!deprecatedDnses.isEmpty()) {
|
||||
for (Inet6Address dns : deprecatedDnses) {
|
||||
final String dnsString = dns.getHostAddress();
|
||||
try {
|
||||
netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// [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 setRaParams(RaParams newParams) {
|
||||
if (mRaDaemon != null) {
|
||||
final RaParams deprecatedParams =
|
||||
RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
|
||||
|
||||
configureLocalRoutes(deprecatedParams.prefixes,
|
||||
(newParams != null) ? newParams.prefixes : null);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
|
||||
synchronized (mLock) {
|
||||
mRaParams = params;
|
||||
assembleRa();
|
||||
if (deprecatedParams != null) {
|
||||
mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
|
||||
mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
|
||||
}
|
||||
|
||||
if (newParams != null) {
|
||||
// Process information that is no longer deprecated.
|
||||
mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
|
||||
mDeprecatedInfoTracker.removeDnses(newParams.dnses);
|
||||
}
|
||||
|
||||
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);
|
||||
boolean shouldSendRA = false;
|
||||
|
||||
putSlla(ra, mHwAddr);
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user