• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.ip;
18 
19 import static android.system.OsConstants.AF_INET6;
20 import static android.system.OsConstants.AF_UNSPEC;
21 import static android.system.OsConstants.IFF_LOOPBACK;
22 
23 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ND_OPTION_PIO;
24 import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ROUTER_ADVERTISEMENT;
25 import static com.android.net.module.util.NetworkStackConstants.INFINITE_LEASE;
26 import static com.android.net.module.util.netlink.NetlinkConstants.IFF_LOWER_UP;
27 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_F_CLONED;
28 import static com.android.net.module.util.netlink.NetlinkConstants.RTN_UNICAST;
29 import static com.android.net.module.util.netlink.NetlinkConstants.RTPROT_KERNEL;
30 import static com.android.net.module.util.netlink.NetlinkConstants.RTPROT_RA;
31 import static com.android.net.module.util.netlink.NetlinkConstants.RT_SCOPE_UNIVERSE;
32 
33 import android.app.AlarmManager;
34 import android.content.Context;
35 import android.net.InetAddresses;
36 import android.net.IpPrefix;
37 import android.net.LinkAddress;
38 import android.net.LinkProperties;
39 import android.net.RouteInfo;
40 import android.os.Handler;
41 import android.os.SystemClock;
42 import android.system.OsConstants;
43 import android.util.Log;
44 
45 import androidx.annotation.NonNull;
46 import androidx.annotation.Nullable;
47 
48 import com.android.internal.annotations.VisibleForTesting;
49 import com.android.net.module.util.HexDump;
50 import com.android.net.module.util.InetAddressUtils;
51 import com.android.net.module.util.InterfaceParams;
52 import com.android.net.module.util.SharedLog;
53 import com.android.net.module.util.ip.NetlinkMonitor;
54 import com.android.net.module.util.netlink.NduseroptMessage;
55 import com.android.net.module.util.netlink.NetlinkConstants;
56 import com.android.net.module.util.netlink.NetlinkMessage;
57 import com.android.net.module.util.netlink.RtNetlinkAddressMessage;
58 import com.android.net.module.util.netlink.RtNetlinkLinkMessage;
59 import com.android.net.module.util.netlink.RtNetlinkPrefixMessage;
60 import com.android.net.module.util.netlink.RtNetlinkRouteMessage;
61 import com.android.net.module.util.netlink.StructIfacacheInfo;
62 import com.android.net.module.util.netlink.StructIfaddrMsg;
63 import com.android.net.module.util.netlink.StructIfinfoMsg;
64 import com.android.net.module.util.netlink.StructNdOptPref64;
65 import com.android.net.module.util.netlink.StructNdOptRdnss;
66 import com.android.net.module.util.netlink.StructPrefixMsg;
67 import com.android.networkstack.apishim.NetworkInformationShimImpl;
68 import com.android.networkstack.apishim.common.NetworkInformationShim;
69 
70 import java.net.Inet6Address;
71 import java.net.InetAddress;
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.Collections;
75 import java.util.HashMap;
76 import java.util.HashSet;
77 import java.util.Set;
78 import java.util.concurrent.TimeUnit;
79 
80 /**
81  * Keeps track of link configuration received from Netd.
82  *
83  * An instance of this class is constructed by passing in an interface name and a callback. When
84  * the class receives update notifications, it applies the update to its local LinkProperties, and
85  * if something has changed, notifies its owner of the update via the callback.
86  *
87  * The owner can then call {@code getLinkProperties()} in order to find out
88  * what changed. If in the meantime the LinkProperties stored here have changed,
89  * this class will return the current LinkProperties. Because each change
90  * triggers an update callback after the change is made, the owner may get more
91  * callbacks than strictly necessary (some of which may be no-ops), but will not
92  * be out of sync once all callbacks have been processed.
93  *
94  * Threading model:
95  *
96  * - The owner of this class is expected to create it, register it, and call
97  *   getLinkProperties or clearLinkProperties on its thread.
98  * - All accesses to mLinkProperties must be synchronized(this). All the other
99  *   member variables are immutable once the object is constructed.
100  *
101  * TODO: Now that all the methods are called on the handler thread, remove synchronization and
102  *       pass the LinkProperties to the update() callback.
103  *
104  * @hide
105  */
106 public class IpClientLinkObserver {
107     private final String mTag;
108 
109     /**
110      * Callback used by {@link IpClientLinkObserver} to send update notifications.
111      */
112     public interface Callback {
113         /**
114          * Called when some properties of the link were updated.
115          *
116          * @param linkState Whether the interface link state is up as per the latest
117          *                  {@link #onInterfaceLinkStateChanged(String, boolean)} callback. This
118          *                  should only be used for metrics purposes, as it could be inconsistent
119          *                  with {@link #getLinkProperties()} in particular.
120          */
update(boolean linkState)121         void update(boolean linkState);
122 
123         /**
124          * Called when an IPv6 address was removed from the interface.
125          *
126          * @param addr The removed IPv6 address.
127          */
onIpv6AddressRemoved(Inet6Address addr)128         void onIpv6AddressRemoved(Inet6Address addr);
129 
130         /**
131          * Called when the clat interface was added/removed.
132          *
133          * @param add True: clat interface was added.
134          *            False: clat interface was removed.
135          */
onClatInterfaceStateUpdate(boolean add)136         void onClatInterfaceStateUpdate(boolean add);
137 
138         /**
139          * Called when the prefix information was updated via RTM_NEWPREFIX netlink message.
140          *
141          * @param info prefix information.
142          */
onNewPrefix(PrefixInfo info)143         void onNewPrefix(PrefixInfo info);
144     }
145 
146     /** Configuration parameters for IpClientLinkObserver. */
147     public static class Configuration {
148         public final int minRdnssLifetime;
149         public final boolean populateLinkAddressLifetime;
150         public final boolean isDhcp6PdPreferredFlagEnabled;
151 
Configuration(int minRdnssLifetime, boolean populateLinkAddressLifetime, boolean isDhcp6PdPreferredFlagEnabled)152         public Configuration(int minRdnssLifetime, boolean populateLinkAddressLifetime,
153                 boolean isDhcp6PdPreferredFlagEnabled) {
154             this.minRdnssLifetime = minRdnssLifetime;
155             this.populateLinkAddressLifetime = populateLinkAddressLifetime;
156             this.isDhcp6PdPreferredFlagEnabled = isDhcp6PdPreferredFlagEnabled;
157         }
158     }
159 
160     /** Prefix information received from RTM_NEWPREFIX netlink message. */
161     public static class PrefixInfo {
162         public final IpPrefix prefix;
163         public short flags;
164         public long preferred;
165         public long valid;
166 
PrefixInfo(@onNull final IpPrefix prefix, short flags, long preferred, long valid)167         public PrefixInfo(@NonNull final IpPrefix prefix, short flags, long preferred, long valid) {
168             this.prefix = prefix;
169             this.flags = flags;
170             this.preferred = preferred;
171             this.valid = valid;
172         }
173     }
174 
175     private final Context mContext;
176     private final String mInterfaceName;
177     private final Callback mCallback;
178     private final LinkProperties mLinkProperties;
179     private boolean mInterfaceLinkState;
180     private DnsServerRepository mDnsServerRepository;
181     private final AlarmManager mAlarmManager;
182     private final Configuration mConfig;
183     private final Handler mHandler;
184     private final IpClient.Dependencies mDependencies;
185     private final String mClatInterfaceName;
186     private final IpClientNetlinkMonitor mNetlinkMonitor;
187     private final NetworkInformationShim mShim;
188     private final AlarmManager.OnAlarmListener mExpirePref64Alarm;
189 
190     private long mNat64PrefixExpiry;
191 
192     /**
193      * Current interface index. Most of this class (and of IpClient), only uses interface names,
194      * not interface indices. This means that the interface index can in theory change, and that
195      * it's not necessarily correct to get the interface name at object creation time (and in
196      * fact, when the object is created, the interface might not even exist).
197      * TODO: once all netlink events pass through this class, stop depending on interface names.
198      */
199     private int mIfindex;
200 
201     // This must match the interface prefix in clatd.c.
202     // TODO: Revert this hack once IpClient and Nat464Xlat work in concert.
203     protected static final String CLAT_PREFIX = "v4-";
204     private static final boolean DBG = true;
205 
206     // The default socket receive buffer size in bytes(4MB). If too many netlink messages are
207     // sent too quickly, those messages can overflow the socket receive buffer. Set a large-enough
208     // recv buffer size to avoid the ENOBUFS as much as possible.
209     @VisibleForTesting
210     static final String CONFIG_SOCKET_RECV_BUFSIZE = "ipclient_netlink_sock_recv_buf_size";
211     @VisibleForTesting
212     static final int SOCKET_RECV_BUFSIZE = 4 * 1024 * 1024;
213 
IpClientLinkObserver(Context context, Handler h, String iface, Callback callback, Configuration config, SharedLog log, IpClient.Dependencies deps)214     public IpClientLinkObserver(Context context, Handler h, String iface, Callback callback,
215             Configuration config, SharedLog log, IpClient.Dependencies deps) {
216         mContext = context;
217         mInterfaceName = iface;
218         mClatInterfaceName = CLAT_PREFIX + iface;
219         mTag = "IpClient/" + mInterfaceName;
220         mCallback = callback;
221         mLinkProperties = new LinkProperties();
222         mLinkProperties.setInterfaceName(mInterfaceName);
223         mConfig = config;
224         mHandler = h;
225         mInterfaceLinkState = true; // Assume up by default
226         mDnsServerRepository = new DnsServerRepository(config.minRdnssLifetime);
227         mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
228         mDependencies = deps;
229         mNetlinkMonitor = deps.makeIpClientNetlinkMonitor(h, log, mTag,
230                 getSocketReceiveBufferSize(),
231                 config.isDhcp6PdPreferredFlagEnabled,
232                 (nlMsg, whenMs) -> processNetlinkMessage(nlMsg, whenMs));
233         mShim = NetworkInformationShimImpl.newInstance();
234         mExpirePref64Alarm = new IpClientObserverAlarmListener();
235         mHandler.post(() -> {
236             if (!mNetlinkMonitor.start()) {
237                 Log.wtf(mTag, "Fail to start NetlinkMonitor.");
238             }
239         });
240     }
241 
shutdown()242     public void shutdown() {
243         mHandler.post(mNetlinkMonitor::stop);
244     }
245 
maybeLog(String operation, String iface, LinkAddress address)246     private void maybeLog(String operation, String iface, LinkAddress address) {
247         if (DBG) {
248             Log.d(mTag, operation + ": " + address + " on " + iface
249                     + " flags " + "0x" + HexDump.toHexString(address.getFlags())
250                     + " scope " + address.getScope());
251         }
252     }
253 
maybeLog(String operation, int ifindex, LinkAddress address)254     private void maybeLog(String operation, int ifindex, LinkAddress address) {
255         maybeLog(operation, "ifindex " + ifindex, address);
256     }
257 
maybeLog(String operation, Object o)258     private void maybeLog(String operation, Object o) {
259         if (DBG) {
260             Log.d(mTag, operation + ": " + o.toString());
261         }
262     }
263 
getSocketReceiveBufferSize()264     private int getSocketReceiveBufferSize() {
265         final int size = mDependencies.getDeviceConfigPropertyInt(CONFIG_SOCKET_RECV_BUFSIZE,
266                 SOCKET_RECV_BUFSIZE /* default value */);
267         if (size < 0) {
268             throw new IllegalArgumentException("Invalid SO_RCVBUF " + size);
269         }
270         return size;
271     }
272 
updateInterfaceLinkStateChanged(boolean state)273     private synchronized void updateInterfaceLinkStateChanged(boolean state) {
274         setInterfaceLinkStateLocked(state);
275     }
276 
updateInterfaceDnsServerInfo(long lifetime, final String[] addresses)277     private void updateInterfaceDnsServerInfo(long lifetime, final String[] addresses) {
278         final boolean changed = mDnsServerRepository.addServers(lifetime, addresses);
279         final boolean linkState;
280         if (changed) {
281             maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses));
282             synchronized (this) {
283                 mDnsServerRepository.setDnsServersOn(mLinkProperties);
284                 linkState = getInterfaceLinkStateLocked();
285             }
286             mCallback.update(linkState);
287         }
288     }
289 
updateInterfaceAddress(@onNull final LinkAddress address, boolean add)290     private boolean updateInterfaceAddress(@NonNull final LinkAddress address, boolean add) {
291         final boolean changed;
292         final boolean linkState;
293         synchronized (this) {
294             if (add) {
295                 changed = mLinkProperties.addLinkAddress(address);
296             } else {
297                 changed = mLinkProperties.removeLinkAddress(address);
298             }
299             linkState = getInterfaceLinkStateLocked();
300         }
301         if (changed) {
302             mCallback.update(linkState);
303             if (!add && address.isIpv6()) {
304                 final Inet6Address addr = (Inet6Address) address.getAddress();
305                 mCallback.onIpv6AddressRemoved(addr);
306             }
307         }
308         return changed;
309     }
310 
updateInterfaceRoute(final RouteInfo route, boolean add)311     private boolean updateInterfaceRoute(final RouteInfo route, boolean add) {
312         final boolean changed;
313         final boolean linkState;
314         synchronized (this) {
315             if (add) {
316                 changed = mLinkProperties.addRoute(route);
317             } else {
318                 changed = mLinkProperties.removeRoute(route);
319             }
320             linkState = getInterfaceLinkStateLocked();
321         }
322         if (changed) {
323             mCallback.update(linkState);
324         }
325         return changed;
326     }
327 
updateInterfaceRemoved()328     private void updateInterfaceRemoved() {
329         // Our interface was removed. Clear our LinkProperties and tell our owner that they are
330         // now empty. Note that from the moment that the interface is removed, any further
331         // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd
332         // code that parses them will not be able to resolve the ifindex to an interface name.
333         final boolean linkState;
334         synchronized (this) {
335             clearLinkProperties();
336             linkState = getInterfaceLinkStateLocked();
337         }
338         mCallback.update(linkState);
339     }
340 
341     /**
342      * Returns a copy of this object's LinkProperties.
343      */
getLinkProperties()344     public synchronized LinkProperties getLinkProperties() {
345         return new LinkProperties(mLinkProperties);
346     }
347 
348     /**
349      * Reset this object's LinkProperties.
350      */
clearLinkProperties()351     public synchronized void clearLinkProperties() {
352         // Clear the repository before clearing mLinkProperties. That way, if a clear() happens
353         // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in
354         // mLinkProperties, as desired.
355         mDnsServerRepository = new DnsServerRepository(mConfig.minRdnssLifetime);
356         cancelPref64Alarm();
357         mLinkProperties.clear();
358         mLinkProperties.setInterfaceName(mInterfaceName);
359     }
360 
getInterfaceLinkStateLocked()361     private boolean getInterfaceLinkStateLocked() {
362         return mInterfaceLinkState;
363     }
364 
setInterfaceLinkStateLocked(boolean state)365     private void setInterfaceLinkStateLocked(boolean state) {
366         mInterfaceLinkState = state;
367     }
368 
369     /** Notifies this object of new interface parameters. */
setInterfaceParams(InterfaceParams params)370     public void setInterfaceParams(InterfaceParams params) {
371         setIfindex(params.index);
372     }
373 
374     /** Notifies this object not to listen on any interface. */
clearInterfaceParams()375     public void clearInterfaceParams() {
376         setIfindex(0);  // 0 is never a valid ifindex.
377     }
378 
setIfindex(int ifindex)379     private void setIfindex(int ifindex) {
380         if (!mNetlinkMonitor.isRunning()) {
381             Log.wtf(mTag, "NetlinkMonitor is not running when setting interface parameter!");
382         }
383         mIfindex = ifindex;
384     }
385 
isSupportedRouteProtocol(RtNetlinkRouteMessage msg)386     private static boolean isSupportedRouteProtocol(RtNetlinkRouteMessage msg) {
387         // Checks whether the protocol is supported. The behaviour is defined by the legacy
388         // implementation in NetlinkEvent.cpp.
389         return msg.getRtMsgHeader().protocol == RTPROT_KERNEL
390                 || msg.getRtMsgHeader().protocol == RTPROT_RA;
391     }
392 
isGlobalUnicastRoute(RtNetlinkRouteMessage msg)393     private static boolean isGlobalUnicastRoute(RtNetlinkRouteMessage msg) {
394         return msg.getRtMsgHeader().scope == RT_SCOPE_UNIVERSE
395                 && msg.getRtMsgHeader().type == RTN_UNICAST;
396     }
397 
398     /**
399      * Simple NetlinkMonitor. Listen for netlink events from kernel.
400      * All methods except the constructor must be called on the handler thread.
401      */
402     static class IpClientNetlinkMonitor extends NetlinkMonitor {
403         /**
404          * An interface used to process the received netlink messages, which is easiler to inject
405          * the function in unit test.
406          */
407         public interface INetlinkMessageProcessor {
processNetlinkMessage(@onNull NetlinkMessage nlMsg, long whenMs)408             void processNetlinkMessage(@NonNull NetlinkMessage nlMsg, long whenMs);
409         }
410 
411         private final Handler mHandler;
412         private final INetlinkMessageProcessor mNetlinkMessageProcessor;
413         private static final int NETLINK_MONITOR_BIND_GROUPS =
414                 NetlinkConstants.RTMGRP_ND_USEROPT
415                         | NetlinkConstants.RTMGRP_LINK
416                         | NetlinkConstants.RTMGRP_IPV4_IFADDR
417                         | NetlinkConstants.RTMGRP_IPV6_IFADDR
418                         | NetlinkConstants.RTMGRP_IPV6_ROUTE;
419 
IpClientNetlinkMonitor(Handler h, SharedLog log, String tag, int sockRcvbufSize, boolean isDhcp6PdPreferredFlagEnabled, INetlinkMessageProcessor p)420         IpClientNetlinkMonitor(Handler h, SharedLog log, String tag, int sockRcvbufSize,
421                 boolean isDhcp6PdPreferredFlagEnabled, INetlinkMessageProcessor p) {
422             super(h, log, tag, OsConstants.NETLINK_ROUTE,
423                     isDhcp6PdPreferredFlagEnabled
424                             ? NETLINK_MONITOR_BIND_GROUPS | NetlinkConstants.RTMGRP_IPV6_PREFIX
425                             : NETLINK_MONITOR_BIND_GROUPS,
426                     sockRcvbufSize);
427             mHandler = h;
428             mNetlinkMessageProcessor = p;
429         }
430 
431         @Override
processNetlinkMessage(NetlinkMessage nlMsg, long whenMs)432         protected void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
433             mNetlinkMessageProcessor.processNetlinkMessage(nlMsg, whenMs);
434         }
435 
isRunning()436         protected boolean isRunning() {
437             return super.isRunning();
438         }
439     }
440 
441     private class IpClientObserverAlarmListener implements AlarmManager.OnAlarmListener {
442         @Override
onAlarm()443         public void onAlarm() {
444             // Ignore the alarm if cancelPref64Alarm has already been called.
445             //
446             // TODO: in the rare case where the alarm fires and posts the lambda to the handler
447             // thread while we are processing an RA that changes the lifetime of the same prefix,
448             // this code will run anyway even if the alarm is rescheduled or cancelled. If the
449             // lifetime in the RA is zero this code will correctly do nothing, but if the lifetime
450             // is nonzero then the prefix will be added and immediately removed by this code.
451             if (mNat64PrefixExpiry == 0) return;
452             updatePref64(mShim.getNat64Prefix(mLinkProperties), mNat64PrefixExpiry,
453                     mNat64PrefixExpiry);
454         }
455     }
456 
cancelPref64Alarm()457     private void cancelPref64Alarm() {
458         // Clear the expiry in case the alarm just fired and has not been processed yet.
459         if (mNat64PrefixExpiry == 0) return;
460         mNat64PrefixExpiry = 0;
461         mAlarmManager.cancel(mExpirePref64Alarm);
462     }
463 
schedulePref64Alarm()464     private void schedulePref64Alarm() {
465         // There is no need to cancel any existing alarms, because we are using the same
466         // OnAlarmListener object, and each such listener can only have at most one alarm.
467         final String tag = mTag + ".PREF64";
468         mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, mNat64PrefixExpiry, tag,
469                 mExpirePref64Alarm, mHandler);
470     }
471 
472     /**
473      * Processes a PREF64 ND option.
474      *
475      * @param prefix The NAT64 prefix.
476      * @param now The time (as determined by SystemClock.elapsedRealtime) when the event
477      *            that triggered this method was received.
478      * @param expiry The time (as determined by SystemClock.elapsedRealtime) when the option
479      *               expires.
480      */
updatePref64(IpPrefix prefix, final long now, final long expiry)481     private synchronized void updatePref64(IpPrefix prefix, final long now,
482             final long expiry) {
483         final IpPrefix currentPrefix = mShim.getNat64Prefix(mLinkProperties);
484 
485         // If the prefix matches the current prefix, refresh its lifetime.
486         if (prefix.equals(currentPrefix)) {
487             mNat64PrefixExpiry = expiry;
488             if (expiry > now) schedulePref64Alarm();
489         }
490 
491         // If we already have a prefix, continue using it and ignore the new one. Stopping and
492         // restarting clatd is disruptive because it will break existing IPv4 connections.
493         // Note: this means that if we receive an RA that adds a new prefix and deletes the old
494         // prefix, we might receive and ignore the new prefix, then delete the old prefix, and
495         // have no prefix until the next RA is received. This is because the kernel returns ND
496         // user options one at a time even if they are in the same RA.
497         // TODO: keep track of the last few prefixes seen, like DnsServerRepository does.
498         if (mNat64PrefixExpiry > now) return;
499 
500         // The current prefix has expired. Either replace it with the new one or delete it.
501         if (expiry > now) {
502             // If expiry > now, then prefix != currentPrefix (due to the return statement above)
503             mShim.setNat64Prefix(mLinkProperties, prefix);
504             mNat64PrefixExpiry = expiry;
505             schedulePref64Alarm();
506         } else {
507             mShim.setNat64Prefix(mLinkProperties, null);
508             cancelPref64Alarm();
509         }
510 
511         mCallback.update(getInterfaceLinkStateLocked());
512     }
513 
processPref64Option(StructNdOptPref64 opt, final long now)514     private void processPref64Option(StructNdOptPref64 opt, final long now) {
515         final long expiry = now + TimeUnit.SECONDS.toMillis(opt.lifetime);
516         updatePref64(opt.prefix, now, expiry);
517     }
518 
processRdnssOption(StructNdOptRdnss opt)519     private void processRdnssOption(StructNdOptRdnss opt) {
520         final String[] addresses = new String[opt.servers.length];
521         for (int i = 0; i < opt.servers.length; i++) {
522             final Inet6Address addr = InetAddressUtils.withScopeId(opt.servers[i], mIfindex);
523             addresses[i] = addr.getHostAddress();
524         }
525         updateInterfaceDnsServerInfo(opt.header.lifetime, addresses);
526     }
527 
processNduseroptMessage(NduseroptMessage msg, final long whenMs)528     private void processNduseroptMessage(NduseroptMessage msg, final long whenMs) {
529         if (msg.family != AF_INET6 || msg.option == null || msg.ifindex != mIfindex) return;
530         if (msg.icmp_type != (byte) ICMPV6_ROUTER_ADVERTISEMENT) return;
531 
532         switch (msg.option.type) {
533             case StructNdOptPref64.TYPE:
534                 processPref64Option((StructNdOptPref64) msg.option, whenMs);
535                 break;
536 
537             case StructNdOptRdnss.TYPE:
538                 processRdnssOption((StructNdOptRdnss) msg.option);
539                 break;
540 
541             default:
542                 // TODO: implement DNSSL.
543                 break;
544         }
545     }
546 
updateClatInterfaceLinkState(@ullable final String ifname, short nlMsgType)547     private void updateClatInterfaceLinkState(@Nullable final String ifname, short nlMsgType) {
548         switch (nlMsgType) {
549             case NetlinkConstants.RTM_NEWLINK:
550                 mCallback.onClatInterfaceStateUpdate(true /* add interface */);
551                 break;
552 
553             case NetlinkConstants.RTM_DELLINK:
554                 mCallback.onClatInterfaceStateUpdate(false /* remove interface */);
555                 break;
556 
557             default:
558                 Log.e(mTag, "unsupported rtnetlink link msg type " + nlMsgType);
559                 break;
560         }
561     }
562 
processRtNetlinkLinkMessage(RtNetlinkLinkMessage msg)563     private void processRtNetlinkLinkMessage(RtNetlinkLinkMessage msg) {
564         // Check if receiving netlink link state update for clat interface.
565         final String ifname = msg.getInterfaceName();
566         final short nlMsgType = msg.getHeader().nlmsg_type;
567         final StructIfinfoMsg ifinfoMsg = msg.getIfinfoHeader();
568         if (mClatInterfaceName.equals(ifname)) {
569             updateClatInterfaceLinkState(ifname, nlMsgType);
570             return;
571         }
572 
573         if (ifinfoMsg.family != AF_UNSPEC || ifinfoMsg.index != mIfindex) return;
574         if ((ifinfoMsg.flags & IFF_LOOPBACK) != 0) return;
575 
576         switch (nlMsgType) {
577             case NetlinkConstants.RTM_NEWLINK:
578                 final boolean state = (ifinfoMsg.flags & IFF_LOWER_UP) != 0;
579                 maybeLog("interfaceLinkStateChanged", "ifindex " + mIfindex
580                         + (state ? " up" : " down"));
581                 updateInterfaceLinkStateChanged(state);
582                 break;
583 
584             case NetlinkConstants.RTM_DELLINK:
585                 maybeLog("interfaceRemoved", ifname);
586                 updateInterfaceRemoved();
587                 break;
588 
589             default:
590                 Log.e(mTag, "Unknown rtnetlink link msg type " + nlMsgType);
591                 break;
592         }
593     }
594 
processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg)595     private void processRtNetlinkAddressMessage(RtNetlinkAddressMessage msg) {
596         final StructIfaddrMsg ifaddrMsg = msg.getIfaddrHeader();
597         if (ifaddrMsg.index != mIfindex) return;
598 
599         final StructIfacacheInfo cacheInfo = msg.getIfacacheInfo();
600         long deprecationTime = LinkAddress.LIFETIME_UNKNOWN;
601         long expirationTime = LinkAddress.LIFETIME_UNKNOWN;
602         if (cacheInfo != null && mConfig.populateLinkAddressLifetime) {
603             deprecationTime = LinkAddress.LIFETIME_PERMANENT;
604             expirationTime = LinkAddress.LIFETIME_PERMANENT;
605 
606             final long now = SystemClock.elapsedRealtime();
607             // TODO: change INFINITE_LEASE to long so the pesky conversions can be removed.
608             if (cacheInfo.preferred < Integer.toUnsignedLong(INFINITE_LEASE)) {
609                 deprecationTime = now + (cacheInfo.preferred /* seconds */  * 1000);
610             }
611             if (cacheInfo.valid < Integer.toUnsignedLong(INFINITE_LEASE)) {
612                 expirationTime = now + (cacheInfo.valid /* seconds */  * 1000);
613             }
614         }
615 
616         final LinkAddress la = new LinkAddress(msg.getIpAddress(), ifaddrMsg.prefixLen,
617                 msg.getFlags(), ifaddrMsg.scope, deprecationTime, expirationTime);
618 
619         switch (msg.getHeader().nlmsg_type) {
620             case NetlinkConstants.RTM_NEWADDR:
621                 if (updateInterfaceAddress(la, true /* add address */)) {
622                     maybeLog("addressUpdated", mIfindex, la);
623                 }
624                 break;
625             case NetlinkConstants.RTM_DELADDR:
626                 if (updateInterfaceAddress(la, false /* remove address */)) {
627                     maybeLog("addressRemoved", mIfindex, la);
628                 }
629                 break;
630             default:
631                 Log.e(mTag, "Unknown rtnetlink address msg type " + msg.getHeader().nlmsg_type);
632         }
633     }
634 
processRtNetlinkRouteMessage(RtNetlinkRouteMessage msg)635     private void processRtNetlinkRouteMessage(RtNetlinkRouteMessage msg) {
636         if (msg.getInterfaceIndex() != mIfindex) return;
637         // Ignore the unsupported route protocol and non-global unicast routes.
638         if (!isSupportedRouteProtocol(msg)
639                 || !isGlobalUnicastRoute(msg)
640                 // don't support source routing
641                 || (msg.getRtMsgHeader().srcLen != 0)
642                 // don't support cloned routes
643                 || ((msg.getRtMsgHeader().flags & RTM_F_CLONED) != 0)) {
644             return;
645         }
646 
647         final RouteInfo route = new RouteInfo(msg.getDestination(), msg.getGateway(),
648                 mInterfaceName, msg.getRtMsgHeader().type);
649         switch (msg.getHeader().nlmsg_type) {
650             case NetlinkConstants.RTM_NEWROUTE:
651                 if (updateInterfaceRoute(route, true /* add route */)) {
652                     maybeLog("routeUpdated", route);
653                 }
654                 break;
655             case NetlinkConstants.RTM_DELROUTE:
656                 if (updateInterfaceRoute(route, false /* remove route */)) {
657                     maybeLog("routeRemoved", route);
658                 }
659                 break;
660             default:
661                 Log.e(mTag, "Unknown rtnetlink route msg type " + msg.getHeader().nlmsg_type);
662                 break;
663         }
664     }
665 
processRtNetlinkPrefixMessage(RtNetlinkPrefixMessage msg)666     private void processRtNetlinkPrefixMessage(RtNetlinkPrefixMessage msg) {
667         final StructPrefixMsg prefixmsg = msg.getPrefixMsg();
668         if (prefixmsg.prefix_family != AF_INET6) return;
669         if (prefixmsg.prefix_ifindex != mIfindex) return;
670         if (prefixmsg.prefix_type != ICMPV6_ND_OPTION_PIO) return;
671         final PrefixInfo info = new PrefixInfo(msg.getPrefix(),
672                 prefixmsg.prefix_flags,
673                 msg.getPreferredLifetime(),
674                 msg.getValidLifetime());
675         mCallback.onNewPrefix(info);
676     }
677 
processNetlinkMessage(NetlinkMessage nlMsg, long whenMs)678     private void processNetlinkMessage(NetlinkMessage nlMsg, long whenMs) {
679         if (nlMsg instanceof NduseroptMessage) {
680             processNduseroptMessage((NduseroptMessage) nlMsg, whenMs);
681         } else if (nlMsg instanceof RtNetlinkLinkMessage) {
682             processRtNetlinkLinkMessage((RtNetlinkLinkMessage) nlMsg);
683         } else if (nlMsg instanceof RtNetlinkAddressMessage) {
684             processRtNetlinkAddressMessage((RtNetlinkAddressMessage) nlMsg);
685         } else if (nlMsg instanceof RtNetlinkRouteMessage) {
686             processRtNetlinkRouteMessage((RtNetlinkRouteMessage) nlMsg);
687         } else if (nlMsg instanceof RtNetlinkPrefixMessage) {
688             processRtNetlinkPrefixMessage((RtNetlinkPrefixMessage) nlMsg);
689         } else {
690             Log.e(mTag, "Unknown netlink message: " + nlMsg);
691         }
692     }
693 
694     /**
695      * Tracks DNS server updates received from Netlink.
696      *
697      * The network may announce an arbitrary number of DNS servers in Router Advertisements at any
698      * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be
699      * used any more. In this way, the network can gracefully migrate clients from one set of DNS
700      * servers to another. Announcements can both raise and lower the lifetime, and an announcement
701      * can expire servers by announcing them with a lifetime of zero.
702      *
703      * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of
704      * DNS servers at any given time. These are referred to as the current servers. In case all the
705      * current servers expire, the class also keeps track of a larger (but limited) number of
706      * servers that are promoted to current servers when the current ones expire. In order to
707      * minimize updates to the rest of the system (and potentially expensive cache flushes) this
708      * class attempts to keep the list of current servers constant where possible. More
709      * specifically, the list of current servers is only updated if a new server is learned and
710      * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the
711      * current servers expires or is pushed out of the set. Therefore, the current servers will not
712      * necessarily be the ones with the highest lifetime, but the ones learned first.
713      *
714      * This is by design: if instead the class always preferred the servers with the highest
715      * lifetime, a (misconfigured?) network where two or more routers announce more than
716      * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations.
717      *
718      * TODO: Currently servers are only expired when a new DNS update is received.
719      * Update them using timers, or possibly on every notification received by NetlinkTracker.
720      *
721      * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink
722      * notifications are sent by multiple threads. If future threads use alarms to expire, those
723      * alarms must also be synchronized(this).
724      *
725      */
726     private static class DnsServerRepository {
727 
728         /** How many DNS servers we will use. 3 is suggested by RFC 6106. */
729         static final int NUM_CURRENT_SERVERS = 3;
730 
731         /** How many DNS servers we'll keep track of, in total. */
732         static final int NUM_SERVERS = 12;
733 
734         /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */
735         private Set<InetAddress> mCurrentServers;
736 
737         public static final String TAG = "DnsServerRepository";
738 
739         /**
740          * Stores all the DNS servers we know about, for use when the current servers expire.
741          * Always sorted in order of decreasing expiry. The elements in this list are also the
742          * values of mIndex, and may be elements in mCurrentServers.
743          */
744         private ArrayList<DnsServerEntry> mAllServers;
745 
746         /**
747          * Indexes the servers so we can update their lifetimes more quickly in the common case
748          * where servers are not being added, but only being refreshed.
749          */
750         private HashMap<InetAddress, DnsServerEntry> mIndex;
751 
752         /**
753          * Minimum (non-zero) RDNSS lifetime to accept.
754          */
755         private final int mMinLifetime;
756 
DnsServerRepository(int minLifetime)757         DnsServerRepository(int minLifetime) {
758             mCurrentServers = new HashSet<>();
759             mAllServers = new ArrayList<>(NUM_SERVERS);
760             mIndex = new HashMap<>(NUM_SERVERS);
761             mMinLifetime = minLifetime;
762         }
763 
764         /** Sets the DNS servers of the provided LinkProperties object to the current servers. */
setDnsServersOn(LinkProperties lp)765         public synchronized void setDnsServersOn(LinkProperties lp) {
766             lp.setDnsServers(mCurrentServers);
767         }
768 
769         /**
770          * Notifies the class of new DNS server information.
771          * @param lifetime the time in seconds that the DNS servers are valid.
772          * @param addresses the string representations of the IP addresses of DNS servers to use.
773          */
addServers(long lifetime, String[] addresses)774         public synchronized boolean addServers(long lifetime, String[] addresses) {
775             // If the servers are below the minimum lifetime, don't change anything.
776             if (lifetime != 0 && lifetime < mMinLifetime) return false;
777 
778             // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned.
779             // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds
780             // (136 years) is close enough.
781             long now = System.currentTimeMillis();
782             long expiry = now + 1000 * lifetime;
783 
784             // Go through the list of servers. For each one, update the entry if one exists, and
785             // create one if it doesn't.
786             for (String addressString : addresses) {
787                 InetAddress address;
788                 try {
789                     address = InetAddresses.parseNumericAddress(addressString);
790                 } catch (IllegalArgumentException ex) {
791                     continue;
792                 }
793 
794                 if (!updateExistingEntry(address, expiry)) {
795                     // There was no entry for this server. Create one, unless it's already expired
796                     // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned).
797                     if (expiry > now) {
798                         DnsServerEntry entry = new DnsServerEntry(address, expiry);
799                         mAllServers.add(entry);
800                         mIndex.put(address, entry);
801                     }
802                 }
803             }
804 
805             // Sort the servers by expiry.
806             Collections.sort(mAllServers);
807 
808             // Prune excess entries and update the current server list.
809             return updateCurrentServers();
810         }
811 
updateExistingEntry(InetAddress address, long expiry)812         private synchronized boolean updateExistingEntry(InetAddress address, long expiry) {
813             DnsServerEntry existing = mIndex.get(address);
814             if (existing != null) {
815                 existing.expiry = expiry;
816                 return true;
817             }
818             return false;
819         }
820 
updateCurrentServers()821         private synchronized boolean updateCurrentServers() {
822             long now = System.currentTimeMillis();
823             boolean changed = false;
824 
825             // Prune excess or expired entries.
826             for (int i = mAllServers.size() - 1; i >= 0; i--) {
827                 if (i >= NUM_SERVERS || mAllServers.get(i).expiry <= now) {
828                     DnsServerEntry removed = mAllServers.remove(i);
829                     mIndex.remove(removed.address);
830                     changed |= mCurrentServers.remove(removed.address);
831                 } else {
832                     break;
833                 }
834             }
835 
836             // Add servers to the current set, in order of decreasing lifetime, until it has enough.
837             // Prefer existing servers over new servers in order to minimize updates to the rest of
838             // the system and avoid persistent oscillations.
839             for (DnsServerEntry entry : mAllServers) {
840                 if (mCurrentServers.size() < NUM_CURRENT_SERVERS) {
841                     changed |= mCurrentServers.add(entry.address);
842                 } else {
843                     break;
844                 }
845             }
846             return changed;
847         }
848     }
849 
850     /**
851      * Represents a DNS server entry with an expiry time.
852      *
853      * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first.
854      * The ordering of entries with the same lifetime is unspecified, because given two servers with
855      * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much
856      * faster than comparing the IP address as well.
857      *
858      * Note: this class has a natural ordering that is inconsistent with equals.
859      */
860     private static class DnsServerEntry implements Comparable<DnsServerEntry> {
861         /** The IP address of the DNS server. */
862         public final InetAddress address;
863         /** The time until which the DNS server may be used. A Java millisecond time as might be
864          * returned by currentTimeMillis(). */
865         public long expiry;
866 
DnsServerEntry(InetAddress address, long expiry)867         DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException {
868             this.address = address;
869             this.expiry = expiry;
870         }
871 
compareTo(DnsServerEntry other)872         public int compareTo(DnsServerEntry other) {
873             return Long.compare(other.expiry, this.expiry);
874         }
875     }
876 }
877