Loading src/android/net/ip/IpClient.java +2 −1 Original line number Diff line number Diff line Loading @@ -582,9 +582,10 @@ public class IpClient extends StateMachine { mMinRdnssLifetimeSec); mLinkObserver = new IpClientLinkObserver( mContext, getHandler(), mInterfaceName, () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config, getHandler(), mLog) { config, mLog) { @Override public void onInterfaceAdded(String iface) { super.onInterfaceAdded(iface); Loading src/android/net/ip/IpClientLinkObserver.java +34 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.system.OsConstants.AF_INET6; import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; import android.app.AlarmManager; import android.content.Context; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; Loading Loading @@ -104,14 +106,15 @@ public class IpClientLinkObserver implements NetworkObserver { private final Callback mCallback; private final LinkProperties mLinkProperties; private DnsServerRepository mDnsServerRepository; private final AlarmManager mAlarmManager; private final Configuration mConfig; private final MyNetlinkMonitor mNetlinkMonitor; private static final boolean DBG = false; public IpClientLinkObserver(String iface, Callback callback, Configuration config, Handler h, SharedLog log) { public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, Configuration config, SharedLog log) { mInterfaceName = iface; mTag = "NetlinkTracker/" + mInterfaceName; mCallback = callback; Loading @@ -119,6 +122,7 @@ public class IpClientLinkObserver implements NetworkObserver { mLinkProperties.setInterfaceName(mInterfaceName); mConfig = config; mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); h.post(mNetlinkMonitor::start); } Loading Loading @@ -253,8 +257,11 @@ public class IpClientLinkObserver implements NetworkObserver { * All methods except the constructor must be called on the handler thread. */ private class MyNetlinkMonitor extends NetlinkMonitor { private final Handler mHandler; MyNetlinkMonitor(Handler h, SharedLog log, String tag) { super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT); mHandler = h; } private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); Loading @@ -274,6 +281,23 @@ public class IpClientLinkObserver implements NetworkObserver { mIfindex = ifindex; } private final AlarmManager.OnAlarmListener mExpirePref64Alarm = () -> { updatePref64(mShim.getNat64Prefix(mLinkProperties), mNat64PrefixExpiry, mNat64PrefixExpiry); }; private void cancelPref64Alarm() { mAlarmManager.cancel(mExpirePref64Alarm); } private void schedulePref64Alarm() { // There is no need to cancel any existing alarms, because we are using the same // OnAlarmListener object, and each such listener can only have at most one alarm. final String tag = mTag + ".PREF64"; mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNat64PrefixExpiry, tag, mExpirePref64Alarm, mHandler); } /** * Processes a PREF64 ND option. * Loading @@ -290,10 +314,16 @@ public class IpClientLinkObserver implements NetworkObserver { // If the prefix matches the current prefix, refresh its lifetime. if (prefix.equals(currentPrefix)) { mNat64PrefixExpiry = expiry; if (expiry > now) { schedulePref64Alarm(); } } // If we already have a prefix, continue using it and ignore the new one. Stopping and // restarting clatd is disruptive because it will break existing IPv4 connections. // TODO: this means that if we receive an RA that adds a new prefix and deletes the old // prefix, we might receive and ignore the new prefix, then delete the old prefix, and // have no prefix until the next RA is received. if (mNat64PrefixExpiry > now) return; // The current prefix has expired. Either replace it with the new one or delete it. Loading @@ -301,14 +331,14 @@ public class IpClientLinkObserver implements NetworkObserver { // If expiry > now, then prefix != currentPrefix (due to the return statement above) mShim.setNat64Prefix(mLinkProperties, prefix); mNat64PrefixExpiry = expiry; schedulePref64Alarm(); } else { mShim.setNat64Prefix(mLinkProperties, null); mNat64PrefixExpiry = 0; cancelPref64Alarm(); } mCallback.update(); // TODO: send a delayed message to remove the prefix when it expires. } private void processPref64Option(StructNdOptPref64 opt, final long now) { Loading tests/integration/src/android/net/ip/IpClientIntegrationTest.java +57 −6 Original line number Diff line number Diff line Loading @@ -60,6 +60,9 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.longThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; Loading @@ -75,6 +78,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.Instrumentation; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -117,6 +121,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; Loading Loading @@ -462,6 +467,28 @@ public class IpClientIntegrationTest { disableIpv6ProvisioningDelays(); } private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) { inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener)); } private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) { // Allow +/- 3 seconds to prevent flaky tests. final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000; final long min = when - 3 * 1000; final long max = when + 3 * 1000; ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class); if (inOrder != null) { inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).setExact( anyInt(), longThat(x -> x >= min && x <= max), contains(tagMatch), captor.capture(), eq(mIpc.getHandler())); } else { verify(mAlarm, timeout(TEST_TIMEOUT_MS)).setExact( anyInt(), longThat(x -> x >= min && x <= max), contains(tagMatch), captor.capture(), eq(mIpc.getHandler())); } return captor.getValue(); } private boolean packetContainsExpectedField(final byte[] packet, final int offset, final byte[] expected) { if (packet.length < offset + expected.length) return false; Loading Loading @@ -1396,8 +1423,7 @@ public class IpClientIntegrationTest { } private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); inOrder.verify(mCb, never()).onLinkPropertiesChange(argThat( inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat( lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); } Loading Loading @@ -1425,11 +1451,16 @@ public class IpClientIntegrationTest { waitForRouterSolicitation(); mPacketReader.sendResponse(ra); InOrder inOrder = inOrder(mCb); // The NAT64 prefix might be detected before or after provisioning success. // Don't test order between these two events. ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); expectAlarmSet(null /*inOrder*/, "PREF64", 600); reset(mCb, mAlarm); // From now on expect events in order. InOrder inOrder = inOrder(mCb, mAlarm); // The NAT64 prefix might have been detected before or after provisioning success. LinkProperties lp = captor.getValue(); if (lp.getNat64Prefix() != null) { assertEquals(prefix, lp.getNat64Prefix()); Loading @@ -1441,30 +1472,50 @@ public class IpClientIntegrationTest { pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800); expectNoNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Withdraw the prefix and expect it to be set to null. pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectAlarmCancelled(inOrder, pref64Alarm); expectNat64PrefixUpdate(inOrder, null); reset(mCb, mAlarm); // Re-announce the prefix. pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectAlarmSet(inOrder, "PREF64", 600); expectNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Announce two prefixes. Don't expect any update because if there is already a NAT64 // prefix, any new prefix is ignored. ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 600).toByteBuffer(); ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectAlarmSet(inOrder, "PREF64", 600); expectNoNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Withdraw the prefix and expect to switch to the new prefix. pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectAlarmCancelled(inOrder, pref64Alarm); // Need a different OnAlarmListener local variable because posting it to the handler in the // lambda below requires it to be final. final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200); expectNat64PrefixUpdate(inOrder, otherPrefix); reset(mCb, mAlarm); // Simulate prefix expiry. mIpc.getHandler().post(() -> lastAlarm.onAlarm()); expectAlarmCancelled(inOrder, pref64Alarm); expectNat64PrefixUpdate(inOrder, null); } private void addIpAddressAndWaitForIt(final String iface) throws Exception { Loading Loading
src/android/net/ip/IpClient.java +2 −1 Original line number Diff line number Diff line Loading @@ -582,9 +582,10 @@ public class IpClient extends StateMachine { mMinRdnssLifetimeSec); mLinkObserver = new IpClientLinkObserver( mContext, getHandler(), mInterfaceName, () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config, getHandler(), mLog) { config, mLog) { @Override public void onInterfaceAdded(String iface) { super.onInterfaceAdded(iface); Loading
src/android/net/ip/IpClientLinkObserver.java +34 −4 Original line number Diff line number Diff line Loading @@ -20,6 +20,8 @@ import static android.system.OsConstants.AF_INET6; import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; import android.app.AlarmManager; import android.content.Context; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; Loading Loading @@ -104,14 +106,15 @@ public class IpClientLinkObserver implements NetworkObserver { private final Callback mCallback; private final LinkProperties mLinkProperties; private DnsServerRepository mDnsServerRepository; private final AlarmManager mAlarmManager; private final Configuration mConfig; private final MyNetlinkMonitor mNetlinkMonitor; private static final boolean DBG = false; public IpClientLinkObserver(String iface, Callback callback, Configuration config, Handler h, SharedLog log) { public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, Configuration config, SharedLog log) { mInterfaceName = iface; mTag = "NetlinkTracker/" + mInterfaceName; mCallback = callback; Loading @@ -119,6 +122,7 @@ public class IpClientLinkObserver implements NetworkObserver { mLinkProperties.setInterfaceName(mInterfaceName); mConfig = config; mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime); mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); h.post(mNetlinkMonitor::start); } Loading Loading @@ -253,8 +257,11 @@ public class IpClientLinkObserver implements NetworkObserver { * All methods except the constructor must be called on the handler thread. */ private class MyNetlinkMonitor extends NetlinkMonitor { private final Handler mHandler; MyNetlinkMonitor(Handler h, SharedLog log, String tag) { super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT); mHandler = h; } private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); Loading @@ -274,6 +281,23 @@ public class IpClientLinkObserver implements NetworkObserver { mIfindex = ifindex; } private final AlarmManager.OnAlarmListener mExpirePref64Alarm = () -> { updatePref64(mShim.getNat64Prefix(mLinkProperties), mNat64PrefixExpiry, mNat64PrefixExpiry); }; private void cancelPref64Alarm() { mAlarmManager.cancel(mExpirePref64Alarm); } private void schedulePref64Alarm() { // There is no need to cancel any existing alarms, because we are using the same // OnAlarmListener object, and each such listener can only have at most one alarm. final String tag = mTag + ".PREF64"; mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNat64PrefixExpiry, tag, mExpirePref64Alarm, mHandler); } /** * Processes a PREF64 ND option. * Loading @@ -290,10 +314,16 @@ public class IpClientLinkObserver implements NetworkObserver { // If the prefix matches the current prefix, refresh its lifetime. if (prefix.equals(currentPrefix)) { mNat64PrefixExpiry = expiry; if (expiry > now) { schedulePref64Alarm(); } } // If we already have a prefix, continue using it and ignore the new one. Stopping and // restarting clatd is disruptive because it will break existing IPv4 connections. // TODO: this means that if we receive an RA that adds a new prefix and deletes the old // prefix, we might receive and ignore the new prefix, then delete the old prefix, and // have no prefix until the next RA is received. if (mNat64PrefixExpiry > now) return; // The current prefix has expired. Either replace it with the new one or delete it. Loading @@ -301,14 +331,14 @@ public class IpClientLinkObserver implements NetworkObserver { // If expiry > now, then prefix != currentPrefix (due to the return statement above) mShim.setNat64Prefix(mLinkProperties, prefix); mNat64PrefixExpiry = expiry; schedulePref64Alarm(); } else { mShim.setNat64Prefix(mLinkProperties, null); mNat64PrefixExpiry = 0; cancelPref64Alarm(); } mCallback.update(); // TODO: send a delayed message to remove the prefix when it expires. } private void processPref64Option(StructNdOptPref64 opt, final long now) { Loading
tests/integration/src/android/net/ip/IpClientIntegrationTest.java +57 −6 Original line number Diff line number Diff line Loading @@ -60,6 +60,9 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.longThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; Loading @@ -75,6 +78,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AlarmManager; import android.app.AlarmManager.OnAlarmListener; import android.app.Instrumentation; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -117,6 +121,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; Loading Loading @@ -462,6 +467,28 @@ public class IpClientIntegrationTest { disableIpv6ProvisioningDelays(); } private void expectAlarmCancelled(InOrder inOrder, OnAlarmListener listener) { inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).cancel(eq(listener)); } private OnAlarmListener expectAlarmSet(InOrder inOrder, String tagMatch, int afterSeconds) { // Allow +/- 3 seconds to prevent flaky tests. final long when = SystemClock.elapsedRealtime() + afterSeconds * 1000; final long min = when - 3 * 1000; final long max = when + 3 * 1000; ArgumentCaptor<OnAlarmListener> captor = ArgumentCaptor.forClass(OnAlarmListener.class); if (inOrder != null) { inOrder.verify(mAlarm, timeout(TEST_TIMEOUT_MS)).setExact( anyInt(), longThat(x -> x >= min && x <= max), contains(tagMatch), captor.capture(), eq(mIpc.getHandler())); } else { verify(mAlarm, timeout(TEST_TIMEOUT_MS)).setExact( anyInt(), longThat(x -> x >= min && x <= max), contains(tagMatch), captor.capture(), eq(mIpc.getHandler())); } return captor.getValue(); } private boolean packetContainsExpectedField(final byte[] packet, final int offset, final byte[] expected) { if (packet.length < offset + expected.length) return false; Loading Loading @@ -1396,8 +1423,7 @@ public class IpClientIntegrationTest { } private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); inOrder.verify(mCb, never()).onLinkPropertiesChange(argThat( inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS).times(0)).onLinkPropertiesChange(argThat( lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); } Loading Loading @@ -1425,11 +1451,16 @@ public class IpClientIntegrationTest { waitForRouterSolicitation(); mPacketReader.sendResponse(ra); InOrder inOrder = inOrder(mCb); // The NAT64 prefix might be detected before or after provisioning success. // Don't test order between these two events. ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); expectAlarmSet(null /*inOrder*/, "PREF64", 600); reset(mCb, mAlarm); // From now on expect events in order. InOrder inOrder = inOrder(mCb, mAlarm); // The NAT64 prefix might have been detected before or after provisioning success. LinkProperties lp = captor.getValue(); if (lp.getNat64Prefix() != null) { assertEquals(prefix, lp.getNat64Prefix()); Loading @@ -1441,30 +1472,50 @@ public class IpClientIntegrationTest { pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); OnAlarmListener pref64Alarm = expectAlarmSet(inOrder, "PREF64", 1800); expectNoNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Withdraw the prefix and expect it to be set to null. pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectAlarmCancelled(inOrder, pref64Alarm); expectNat64PrefixUpdate(inOrder, null); reset(mCb, mAlarm); // Re-announce the prefix. pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectAlarmSet(inOrder, "PREF64", 600); expectNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Announce two prefixes. Don't expect any update because if there is already a NAT64 // prefix, any new prefix is ignored. ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 600).toByteBuffer(); ByteBuffer otherPref64 = new StructNdOptPref64(otherPrefix, 1200).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectAlarmSet(inOrder, "PREF64", 600); expectNoNat64PrefixUpdate(inOrder, prefix); reset(mCb, mAlarm); // Withdraw the prefix and expect to switch to the new prefix. pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectAlarmCancelled(inOrder, pref64Alarm); // Need a different OnAlarmListener local variable because posting it to the handler in the // lambda below requires it to be final. final OnAlarmListener lastAlarm = expectAlarmSet(inOrder, "PREF64", 1200); expectNat64PrefixUpdate(inOrder, otherPrefix); reset(mCb, mAlarm); // Simulate prefix expiry. mIpc.getHandler().post(() -> lastAlarm.onAlarm()); expectAlarmCancelled(inOrder, pref64Alarm); expectNat64PrefixUpdate(inOrder, null); } private void addIpAddressAndWaitForIt(final String iface) throws Exception { Loading