Deprecate IPv6 prefixes no longer in use.
Bug: 30298058
Change-Id: I0fa9ece9b2fb07214971a91b77f5b07972d83bb6
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 a58d243..b47e079 100644
--- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
+++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringInterfaceServices.java
@@ -176,9 +176,21 @@
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);
+ mRaDaemon.buildNewRa(params, deprecated);
}
mLastRaParams = params;
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index 407d315..1a9d2f2 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -45,7 +45,9 @@
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -109,6 +111,11 @@
private final byte[] mRA = new byte[IPV6_MIN_MTU];
@GuardedBy("mLock")
private int mRaLength;
+ @GuardedBy("mLock")
+ private final HashMap<IpPrefix, Integer> mDeprecatedPrefixes;
+
+ @GuardedBy("mLock")
+ private RaParams mRaParams;
private volatile FileDescriptor mSocket;
private volatile MulticastTransmitter mMulticastTransmitter;
@@ -141,45 +148,29 @@
mIfIndex = ifindex;
mHwAddr = hwaddr;
mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
+ mDeprecatedPrefixes = new HashMap<>();
}
- public void buildNewRa(RaParams params) {
+ 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;
}
- if (params.mtu < IPV6_MIN_MTU) {
- params.mtu = IPV6_MIN_MTU;
- }
-
- final ByteBuffer ra = ByteBuffer.wrap(mRA);
- ra.order(ByteOrder.BIG_ENDIAN);
-
synchronized (mLock) {
- try {
- putHeader(ra, params.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);
- putMtu(ra, params.mtu);
- for (IpPrefix ipp : params.prefixes) {
- putPio(ra, ipp);
- }
- if (params.dnses.size() > 0) {
- putRdnss(ra, params.dnses);
- }
- mRaLength = ra.position();
- } catch (BufferOverflowException e) {
- Log.e(TAG, "Could not construct new RA: " + e);
- mRaLength = 0;
- return;
- }
+ mRaParams = params;
+ assembleRa();
}
maybeNotifyMulticastTransmitter();
@@ -216,6 +207,64 @@
}
}
+ private void assembleRa() {
+ 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);
+
+ putMtu(ra, mRaParams.mtu);
+
+ 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);
+ }
+
+ if (mRaParams.dnses.size() > 0) {
+ putRdnss(ra, mRaParams.dnses);
+ }
+
+ mRaLength = ra.position();
+ } catch (BufferOverflowException e) {
+ Log.e(TAG, "Could not construct new RA: " + e);
+ mRaLength = 0;
+ return;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+ }
+
+ return removed;
+ }
+
private void maybeNotifyMulticastTransmitter() {
final MulticastTransmitter m = mMulticastTransmitter;
if (m != null) {
@@ -325,10 +374,11 @@
ra.put(ND_OPTION_MTU)
.put(MTU_NUM_8OCTETS)
.putShort(asShort(0))
- .putInt(mtu);
+ .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
}
- private static void putPio(ByteBuffer ra, IpPrefix ipp) {
+ private static void putPio(ByteBuffer ra, IpPrefix ipp,
+ int validTime, int preferredTime) {
/**
Prefix Information
@@ -359,13 +409,17 @@
final byte ND_OPTION_PIO = 3;
final byte PIO_NUM_8OCTETS = 4;
+ if (validTime < 0) validTime = 0;
+ if (preferredTime < 0) preferredTime = 0;
+ if (preferredTime > validTime) preferredTime = validTime;
+
final byte[] addr = ipp.getAddress().getAddress();
ra.put(ND_OPTION_PIO)
.put(PIO_NUM_8OCTETS)
.put(asByte(prefixLength))
- .put(asByte(0xc0)) // L&A set
- .putInt(DEFAULT_LIFETIME)
- .putInt(DEFAULT_LIFETIME)
+ .put(asByte(0xc0)) /* L & A set */
+ .putInt(validTime)
+ .putInt(preferredTime)
.putInt(0)
.put(addr);
}
@@ -547,6 +601,11 @@
}
maybeSendRA(mAllNodes);
+ if (decrementDeprecatedPrefixes() > 0) {
+ // At least one deprecated PIO has been removed;
+ // reassemble the RA.
+ assembleRa();
+ }
}
}
@@ -560,15 +619,17 @@
}
private int getNextMulticastTransmitDelaySec() {
+ int countDeprecatedPrefixes = 0;
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();
}
final int urgentPending = mUrgentAnnouncements.getAndDecrement();
- if (urgentPending > 0) {
+ if (urgentPending > 0 || countDeprecatedPrefixes > 0) {
return MIN_DELAY_BETWEEN_RAS_SEC;
}