Loading common/netlinkclient/src/android/net/netlink/NetlinkConstants.java +4 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,10 @@ public class NetlinkConstants { /* see <linux_src>/include/uapi/linux/sock_diag.h */ public static final short SOCK_DIAG_BY_FAMILY = 20; // Netlink groups. public static final int RTNLGRP_ND_USEROPT = 20; public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1); public static String stringForNlMsgType(short nlm_type) { switch (nlm_type) { case NLMSG_NOOP: return "NLMSG_NOOP"; Loading src/android/net/ip/IpClient.java +5 −1 Original line number Diff line number Diff line Loading @@ -583,7 +583,8 @@ public class IpClient extends StateMachine { mLinkObserver = new IpClientLinkObserver( mInterfaceName, () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config) { () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config, getHandler(), mLog) { @Override public void onInterfaceAdded(String iface) { super.onInterfaceAdded(iface); Loading Loading @@ -1225,6 +1226,7 @@ public class IpClient extends StateMachine { newLp.addRoute(route); } addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers()); newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix()); // [3] Add in data from DHCPv4, if available. // Loading Loading @@ -1563,6 +1565,7 @@ public class IpClient extends StateMachine { public void enter() { stopAllIP(); mLinkObserver.clearInterfaceParams(); resetLinkProperties(); if (mStartTimeMillis > 0) { // Completed a life-cycle; send a final empty LinkProperties Loading Loading @@ -1712,6 +1715,7 @@ public class IpClient extends StateMachine { transitionTo(mStoppedState); return; } mLinkObserver.setInterfaceParams(mInterfaceParams); mCallback.setNeighborDiscoveryOffload(true); } Loading src/android/net/ip/IpClientLinkObserver.java +127 −2 Original line number Diff line number Diff line Loading @@ -16,12 +16,27 @@ package android.net.ip; import static android.system.OsConstants.AF_INET6; import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; import android.net.netlink.NduseroptMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkMessage; import android.net.netlink.StructNdOptPref64; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.Handler; import android.system.OsConstants; import android.util.Log; import com.android.networkstack.apishim.NetworkInformationShim; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.server.NetworkObserver; import java.net.InetAddress; Loading @@ -31,6 +46,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; /** * Keeps track of link configuration received from Netd. Loading @@ -56,6 +72,10 @@ import java.util.Set; * - All accesses to mLinkProperties must be synchronized(this). All the other * member variables are immutable once the object is constructed. * * TODO: Now that all the methods are called on the handler thread, remove synchronization and * pass the LinkProperties to the update() callback. * TODO: Stop extending NetworkObserver and get events from netlink directly. * * @hide */ public class IpClientLinkObserver implements NetworkObserver { Loading Loading @@ -86,16 +106,21 @@ public class IpClientLinkObserver implements NetworkObserver { private DnsServerRepository mDnsServerRepository; private final Configuration mConfig; private final MyNetlinkMonitor mNetlinkMonitor; private static final boolean DBG = false; public IpClientLinkObserver(String iface, Callback callback, Configuration config) { mTag = "NetlinkTracker/" + iface; public IpClientLinkObserver(String iface, Callback callback, Configuration config, Handler h, SharedLog log) { mInterfaceName = iface; mTag = "NetlinkTracker/" + mInterfaceName; mCallback = callback; mLinkProperties = new LinkProperties(); mLinkProperties.setInterfaceName(mInterfaceName); mConfig = config; mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime); mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); h.post(mNetlinkMonitor::start); } private void maybeLog(String operation, String iface, LinkAddress address) { Loading Loading @@ -213,6 +238,106 @@ public class IpClientLinkObserver implements NetworkObserver { mLinkProperties.setInterfaceName(mInterfaceName); } /** Notifies this object of new interface parameters. */ public void setInterfaceParams(InterfaceParams params) { mNetlinkMonitor.setIfindex(params.index); } /** Notifies this object not to listen on any interface. */ public void clearInterfaceParams() { mNetlinkMonitor.setIfindex(0); // 0 is never a valid ifindex. } /** * Simple NetlinkMonitor. Currently only listens for PREF64 events. * All methods except the constructor must be called on the handler thread. */ private class MyNetlinkMonitor extends NetlinkMonitor { MyNetlinkMonitor(Handler h, SharedLog log, String tag) { super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT); } private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); private long mNat64PrefixExpiry; /** * Current interface index. Most of this class (and of IpClient), only uses interface names, * not interface indices. This means that the interface index can in theory change, and that * it's not necessarily correct to get the interface name at object creation time (and in * fact, when the object is created, the interface might not even exist). * TODO: once all netlink events pass through this class, stop depending on interface names. */ private int mIfindex; void setIfindex(int ifindex) { mIfindex = ifindex; } /** * Processes a PREF64 ND option. * * @param prefix The NAT64 prefix. * @param now The time (as determined by SystemClock.elapsedRealtime) when the event * that triggered this method was received. * @param expiry The time (as determined by SystemClock.elapsedRealtime) when the option * expires. */ private synchronized void updatePref64(IpPrefix prefix, final long now, final long expiry) { final IpPrefix currentPrefix = mShim.getNat64Prefix(mLinkProperties); // If the prefix matches the current prefix, refresh its lifetime. if (prefix.equals(currentPrefix)) { mNat64PrefixExpiry = expiry; } // 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. if (mNat64PrefixExpiry > now) return; // The current prefix has expired. Either replace it with the new one or delete it. if (expiry > now) { // If expiry > now, then prefix != currentPrefix (due to the return statement above) mShim.setNat64Prefix(mLinkProperties, prefix); mNat64PrefixExpiry = expiry; } else { mShim.setNat64Prefix(mLinkProperties, null); mNat64PrefixExpiry = 0; } mCallback.update(); // TODO: send a delayed message to remove the prefix when it expires. } private void processPref64Option(StructNdOptPref64 opt, final long now) { final long expiry = now + TimeUnit.SECONDS.toMillis(opt.lifetime); updatePref64(opt.prefix, now, expiry); } private void processNduseroptMessage(NduseroptMessage msg, final long whenMs) { if (msg.family != AF_INET6 || msg.option == null || msg.ifindex != mIfindex) return; if (msg.icmp_type != (byte) ICMPV6_ROUTER_ADVERTISEMENT) return; switch (msg.option.type) { case StructNdOptPref64.TYPE: processPref64Option((StructNdOptPref64) msg.option, whenMs); break; default: // TODO: implement RDNSS and DNSSL. break; } } @Override protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { if (!(nlMsg instanceof NduseroptMessage)) return; processNduseroptMessage((NduseroptMessage) nlMsg, whenMs); } } /** * Tracks DNS server updates received from Netlink. * Loading tests/integration/src/android/net/ip/IpClientIntegrationTest.java +86 −2 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; Loading Loading @@ -103,6 +104,7 @@ import android.net.dhcp.DhcpRequestPacket; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; import android.net.ipmemorystore.Status; import android.net.netlink.StructNdOptPref64; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.util.InterfaceParams; Loading Loading @@ -138,6 +140,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; Loading Loading @@ -1279,12 +1282,16 @@ public class IpClientIntegrationTest { return packet; } @Test public void testRaRdnss() throws Exception { private void disableRouterSolicitationDelay() throws Exception { // Speed up the test by removing router_solicitation_delay. // We don't need to restore the default value because the interface is removed in tearDown. // TODO: speed up further by not waiting for RA but keying off first IPv6 packet. mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0"); } @Test public void testRaRdnss() throws Exception { disableRouterSolicitationDelay(); ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() Loading Loading @@ -1338,6 +1345,83 @@ public class IpClientIntegrationTest { reset(mCb); } private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception { inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( argThat(lp -> Objects.equals(expected, lp.getNat64Prefix()))); } private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); inOrder.verify(mCb, never()).onLinkPropertiesChange(argThat( lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testPref64Option() throws Exception { disableRouterSolicitationDelay(); ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .withoutIPv4() .build(); mIpc.startProvisioning(config); final String dnsServer = "2001:4860:4860::64"; final IpPrefix prefix = new IpPrefix("64:ff9b::/96"); final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96"); final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); ByteBuffer rdnss = buildRdnssOption(600, dnsServer); ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ByteBuffer ra = buildRaPacket(pio, rdnss, pref64); waitForRouterSolicitation(); mPacketReader.sendResponse(ra); InOrder inOrder = inOrder(mCb); ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); // The NAT64 prefix might have been detected before or after provisioning success. LinkProperties lp = captor.getValue(); if (lp.getNat64Prefix() != null) { assertEquals(prefix, lp.getNat64Prefix()); } else { expectNat64PrefixUpdate(inOrder, prefix); } // Increase the lifetime and expect the prefix not to change. pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectNoNat64PrefixUpdate(inOrder, prefix); // 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); expectNat64PrefixUpdate(inOrder, null); // Re-announce the prefix. pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectNat64PrefixUpdate(inOrder, prefix); // 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(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectNat64PrefixUpdate(inOrder, otherPrefix); } @Test public void testIpClientClearingIpAddressState() throws Exception { final long currentTime = System.currentTimeMillis(); Loading Loading
common/netlinkclient/src/android/net/netlink/NetlinkConstants.java +4 −0 Original line number Diff line number Diff line Loading @@ -106,6 +106,10 @@ public class NetlinkConstants { /* see <linux_src>/include/uapi/linux/sock_diag.h */ public static final short SOCK_DIAG_BY_FAMILY = 20; // Netlink groups. public static final int RTNLGRP_ND_USEROPT = 20; public static final int RTMGRP_ND_USEROPT = 1 << (RTNLGRP_ND_USEROPT - 1); public static String stringForNlMsgType(short nlm_type) { switch (nlm_type) { case NLMSG_NOOP: return "NLMSG_NOOP"; Loading
src/android/net/ip/IpClient.java +5 −1 Original line number Diff line number Diff line Loading @@ -583,7 +583,8 @@ public class IpClient extends StateMachine { mLinkObserver = new IpClientLinkObserver( mInterfaceName, () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config) { () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED), config, getHandler(), mLog) { @Override public void onInterfaceAdded(String iface) { super.onInterfaceAdded(iface); Loading Loading @@ -1225,6 +1226,7 @@ public class IpClient extends StateMachine { newLp.addRoute(route); } addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers()); newLp.setNat64Prefix(netlinkLinkProperties.getNat64Prefix()); // [3] Add in data from DHCPv4, if available. // Loading Loading @@ -1563,6 +1565,7 @@ public class IpClient extends StateMachine { public void enter() { stopAllIP(); mLinkObserver.clearInterfaceParams(); resetLinkProperties(); if (mStartTimeMillis > 0) { // Completed a life-cycle; send a final empty LinkProperties Loading Loading @@ -1712,6 +1715,7 @@ public class IpClient extends StateMachine { transitionTo(mStoppedState); return; } mLinkObserver.setInterfaceParams(mInterfaceParams); mCallback.setNeighborDiscoveryOffload(true); } Loading
src/android/net/ip/IpClientLinkObserver.java +127 −2 Original line number Diff line number Diff line Loading @@ -16,12 +16,27 @@ package android.net.ip; import static android.system.OsConstants.AF_INET6; import static com.android.server.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; import android.net.netlink.NduseroptMessage; import android.net.netlink.NetlinkConstants; import android.net.netlink.NetlinkMessage; import android.net.netlink.StructNdOptPref64; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.Handler; import android.system.OsConstants; import android.util.Log; import com.android.networkstack.apishim.NetworkInformationShim; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.server.NetworkObserver; import java.net.InetAddress; Loading @@ -31,6 +46,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; /** * Keeps track of link configuration received from Netd. Loading @@ -56,6 +72,10 @@ import java.util.Set; * - All accesses to mLinkProperties must be synchronized(this). All the other * member variables are immutable once the object is constructed. * * TODO: Now that all the methods are called on the handler thread, remove synchronization and * pass the LinkProperties to the update() callback. * TODO: Stop extending NetworkObserver and get events from netlink directly. * * @hide */ public class IpClientLinkObserver implements NetworkObserver { Loading Loading @@ -86,16 +106,21 @@ public class IpClientLinkObserver implements NetworkObserver { private DnsServerRepository mDnsServerRepository; private final Configuration mConfig; private final MyNetlinkMonitor mNetlinkMonitor; private static final boolean DBG = false; public IpClientLinkObserver(String iface, Callback callback, Configuration config) { mTag = "NetlinkTracker/" + iface; public IpClientLinkObserver(String iface, Callback callback, Configuration config, Handler h, SharedLog log) { mInterfaceName = iface; mTag = "NetlinkTracker/" + mInterfaceName; mCallback = callback; mLinkProperties = new LinkProperties(); mLinkProperties.setInterfaceName(mInterfaceName); mConfig = config; mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime); mNetlinkMonitor = new MyNetlinkMonitor(h, log, mTag); h.post(mNetlinkMonitor::start); } private void maybeLog(String operation, String iface, LinkAddress address) { Loading Loading @@ -213,6 +238,106 @@ public class IpClientLinkObserver implements NetworkObserver { mLinkProperties.setInterfaceName(mInterfaceName); } /** Notifies this object of new interface parameters. */ public void setInterfaceParams(InterfaceParams params) { mNetlinkMonitor.setIfindex(params.index); } /** Notifies this object not to listen on any interface. */ public void clearInterfaceParams() { mNetlinkMonitor.setIfindex(0); // 0 is never a valid ifindex. } /** * Simple NetlinkMonitor. Currently only listens for PREF64 events. * All methods except the constructor must be called on the handler thread. */ private class MyNetlinkMonitor extends NetlinkMonitor { MyNetlinkMonitor(Handler h, SharedLog log, String tag) { super(h, log, tag, OsConstants.NETLINK_ROUTE, NetlinkConstants.RTMGRP_ND_USEROPT); } private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); private long mNat64PrefixExpiry; /** * Current interface index. Most of this class (and of IpClient), only uses interface names, * not interface indices. This means that the interface index can in theory change, and that * it's not necessarily correct to get the interface name at object creation time (and in * fact, when the object is created, the interface might not even exist). * TODO: once all netlink events pass through this class, stop depending on interface names. */ private int mIfindex; void setIfindex(int ifindex) { mIfindex = ifindex; } /** * Processes a PREF64 ND option. * * @param prefix The NAT64 prefix. * @param now The time (as determined by SystemClock.elapsedRealtime) when the event * that triggered this method was received. * @param expiry The time (as determined by SystemClock.elapsedRealtime) when the option * expires. */ private synchronized void updatePref64(IpPrefix prefix, final long now, final long expiry) { final IpPrefix currentPrefix = mShim.getNat64Prefix(mLinkProperties); // If the prefix matches the current prefix, refresh its lifetime. if (prefix.equals(currentPrefix)) { mNat64PrefixExpiry = expiry; } // 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. if (mNat64PrefixExpiry > now) return; // The current prefix has expired. Either replace it with the new one or delete it. if (expiry > now) { // If expiry > now, then prefix != currentPrefix (due to the return statement above) mShim.setNat64Prefix(mLinkProperties, prefix); mNat64PrefixExpiry = expiry; } else { mShim.setNat64Prefix(mLinkProperties, null); mNat64PrefixExpiry = 0; } mCallback.update(); // TODO: send a delayed message to remove the prefix when it expires. } private void processPref64Option(StructNdOptPref64 opt, final long now) { final long expiry = now + TimeUnit.SECONDS.toMillis(opt.lifetime); updatePref64(opt.prefix, now, expiry); } private void processNduseroptMessage(NduseroptMessage msg, final long whenMs) { if (msg.family != AF_INET6 || msg.option == null || msg.ifindex != mIfindex) return; if (msg.icmp_type != (byte) ICMPV6_ROUTER_ADVERTISEMENT) return; switch (msg.option.type) { case StructNdOptPref64.TYPE: processPref64Option((StructNdOptPref64) msg.option, whenMs); break; default: // TODO: implement RDNSS and DNSSL. break; } } @Override protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) { if (!(nlMsg instanceof NduseroptMessage)) return; processNduseroptMessage((NduseroptMessage) nlMsg, whenMs); } } /** * Tracks DNS server updates received from Netlink. * Loading
tests/integration/src/android/net/ip/IpClientIntegrationTest.java +86 −2 Original line number Diff line number Diff line Loading @@ -65,6 +65,7 @@ import static org.mockito.Mockito.argThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; Loading Loading @@ -103,6 +104,7 @@ import android.net.dhcp.DhcpRequestPacket; import android.net.ipmemorystore.NetworkAttributes; import android.net.ipmemorystore.OnNetworkAttributesRetrievedListener; import android.net.ipmemorystore.Status; import android.net.netlink.StructNdOptPref64; import android.net.shared.ProvisioningConfiguration; import android.net.shared.ProvisioningConfiguration.ScanResultInfo; import android.net.util.InterfaceParams; Loading Loading @@ -138,6 +140,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.Spy; Loading Loading @@ -1279,12 +1282,16 @@ public class IpClientIntegrationTest { return packet; } @Test public void testRaRdnss() throws Exception { private void disableRouterSolicitationDelay() throws Exception { // Speed up the test by removing router_solicitation_delay. // We don't need to restore the default value because the interface is removed in tearDown. // TODO: speed up further by not waiting for RA but keying off first IPv6 packet. mNetd.setProcSysNet(INetd.IPV6, INetd.CONF, mIfaceName, "router_solicitation_delay", "0"); } @Test public void testRaRdnss() throws Exception { disableRouterSolicitationDelay(); ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() Loading Loading @@ -1338,6 +1345,83 @@ public class IpClientIntegrationTest { reset(mCb); } private void expectNat64PrefixUpdate(InOrder inOrder, IpPrefix expected) throws Exception { inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange( argThat(lp -> Objects.equals(expected, lp.getNat64Prefix()))); } private void expectNoNat64PrefixUpdate(InOrder inOrder, IpPrefix unchanged) throws Exception { HandlerUtilsKt.waitForIdle(mIpc.getHandler(), TEST_TIMEOUT_MS); inOrder.verify(mCb, never()).onLinkPropertiesChange(argThat( lp -> !Objects.equals(unchanged, lp.getNat64Prefix()))); } @Test @IgnoreUpTo(Build.VERSION_CODES.Q) public void testPref64Option() throws Exception { disableRouterSolicitationDelay(); ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .withoutIPv4() .build(); mIpc.startProvisioning(config); final String dnsServer = "2001:4860:4860::64"; final IpPrefix prefix = new IpPrefix("64:ff9b::/96"); final IpPrefix otherPrefix = new IpPrefix("2001:db8:64::/96"); final ByteBuffer pio = buildPioOption(600, 300, "2001:db8:1::/64"); ByteBuffer rdnss = buildRdnssOption(600, dnsServer); ByteBuffer pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ByteBuffer ra = buildRaPacket(pio, rdnss, pref64); waitForRouterSolicitation(); mPacketReader.sendResponse(ra); InOrder inOrder = inOrder(mCb); ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); // The NAT64 prefix might have been detected before or after provisioning success. LinkProperties lp = captor.getValue(); if (lp.getNat64Prefix() != null) { assertEquals(prefix, lp.getNat64Prefix()); } else { expectNat64PrefixUpdate(inOrder, prefix); } // Increase the lifetime and expect the prefix not to change. pref64 = new StructNdOptPref64(prefix, 1800).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectNoNat64PrefixUpdate(inOrder, prefix); // 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); expectNat64PrefixUpdate(inOrder, null); // Re-announce the prefix. pref64 = new StructNdOptPref64(prefix, 600).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64); mPacketReader.sendResponse(ra); expectNat64PrefixUpdate(inOrder, prefix); // 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(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); pref64 = new StructNdOptPref64(prefix, 0).toByteBuffer(); ra = buildRaPacket(pio, rdnss, pref64, otherPref64); mPacketReader.sendResponse(ra); expectNat64PrefixUpdate(inOrder, otherPrefix); } @Test public void testIpClientClearingIpAddressState() throws Exception { final long currentTime = System.currentTimeMillis(); Loading