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