diff options
author | Lorenzo Colitti <lorenzo@google.com> | 2020-04-09 21:02:13 +0900 |
---|---|---|
committer | Lorenzo Colitti <lorenzo@google.com> | 2020-04-20 19:45:28 +0900 |
commit | 3a0519aa5578d87dc62fd0645a5c58d36b6dcb6f (patch) | |
tree | 55cccafa676021993c2c03d146c3b7d0c3422a52 /src/android/net | |
parent | 2e0f009fb49ee587861bcef61d8f9468f250a224 (diff) |
Listen for pref64 RA attributes in IpClientLinkObserver.
This allows IpClient to parse the pref64 RA option and put it
in the LinkProperties to be sent to ConnectivityService.
IpClientLinkObserver is a natural place for this because it is
the part of IpClient that is already tasked with receiving
netlink events and storing the results in IpClient's
LinkProperties.
Instead of using the path used by most attributes, which are
parsed by NetlinkHandler, converted to a string array, then
re-parsed and sent over binder call to the networkstack, simply
open a netlink socket in the networkstack process and read the
netlink messages from there. In the future, we can build on this
to parse other netlink messages (e.g., IP addresses, routes,
RDNSS, etc.) in the networkstack and entirely remove the
dependency on netd's NetlinkHandler, which is crufty, hard to
extend, and does not support interface indices.
This means that the pref64 attribute will not be ordered with
respect to other netlink events. This is acceptable because the
pref64 attribute does not need to be ordered with any other
information and its presence or absence does not cause
provisioning to succeed or fail. Today the pref64 is learned
through an entirely different codepath (DNS lookups) and that is
not ordered in any way either.
This CL does not change the threading model: the netlink updates
are processed on the handler thread like all the other updates
seen by IpClientLinkObserver, and all access to mLinkProperties
is synchronized (this). This synchronization is no longer
necessary because everything is on the handler thread anyway,
but that will be cleaned up in a future CL.
Because netlink events contain interface indices, but IpClient
and netd deal with interface names, IpClientLinkObserver must be
told what the interface index is. This is done when
startProvisioning is called, because that is when IpClient
fetches the interface parameters including the MAC address and
interface index. It cannot be done when IpClientLinkObserver is
started, because at that time the interface might not exist, or
might exist with a previous interface index. The interface index
is cleared when IpClient enters the stopped state and the
LinkProperties are cleared.
Bug: 153694684
Test: atest NetworkStackNextIntegrationTests:IpClientIntegrationTest#testPref64Option --iterations 100
Change-Id: I3f8d2fbf2e203c6f90029947fa55b5e0b3b06d94
Diffstat (limited to 'src/android/net')
-rw-r--r-- | src/android/net/ip/IpClient.java | 6 | ||||
-rw-r--r-- | src/android/net/ip/IpClientLinkObserver.java | 129 |
2 files changed, 132 insertions, 3 deletions
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index 4ddcc13..3fb5001 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java @@ -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); @@ -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. // @@ -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 @@ -1712,6 +1715,7 @@ public class IpClient extends StateMachine { transitionTo(mStoppedState); return; } + mLinkObserver.setInterfaceParams(mInterfaceParams); mCallback.setNeighborDiscoveryOffload(true); } diff --git a/src/android/net/ip/IpClientLinkObserver.java b/src/android/net/ip/IpClientLinkObserver.java index 02bf5f0..bbd2176 100644 --- a/src/android/net/ip/IpClientLinkObserver.java +++ b/src/android/net/ip/IpClientLinkObserver.java @@ -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; @@ -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. @@ -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 { @@ -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) { @@ -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. * |