summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2020-04-09 21:02:13 +0900
committerLorenzo Colitti <lorenzo@google.com>2020-04-20 19:45:28 +0900
commit3a0519aa5578d87dc62fd0645a5c58d36b6dcb6f (patch)
tree55cccafa676021993c2c03d146c3b7d0c3422a52 /src
parent2e0f009fb49ee587861bcef61d8f9468f250a224 (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')
-rw-r--r--src/android/net/ip/IpClient.java6
-rw-r--r--src/android/net/ip/IpClientLinkObserver.java129
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.
*