• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.*;
20 
21 import android.net.IpPrefix;
22 import android.net.LinkAddress;
23 import android.net.LinkProperties;
24 import android.net.NetworkUtils;
25 import android.system.ErrnoException;
26 import android.system.Os;
27 import android.system.StructGroupReq;
28 import android.system.StructTimeval;
29 import android.util.Log;
30 
31 import com.android.internal.annotations.GuardedBy;
32 
33 import libcore.io.IoBridge;
34 import libcore.util.HexEncoding;
35 
36 import java.io.FileDescriptor;
37 import java.io.InterruptedIOException;
38 import java.io.IOException;
39 import java.net.Inet6Address;
40 import java.net.InetAddress;
41 import java.net.InetSocketAddress;
42 import java.net.SocketException;
43 import java.net.UnknownHostException;
44 import java.nio.BufferOverflowException;
45 import java.nio.ByteBuffer;
46 import java.nio.ByteOrder;
47 import java.util.ArrayList;
48 import java.util.HashMap;
49 import java.util.HashSet;
50 import java.util.Iterator;
51 import java.util.Map;
52 import java.util.Random;
53 import java.util.Set;
54 import java.util.concurrent.atomic.AtomicInteger;
55 
56 
57 /**
58  * Basic IPv6 Router Advertisement Daemon.
59  *
60  * TODO:
61  *
62  *     - Rewrite using Handler (and friends) so that AlarmManager can deliver
63  *       "kick" messages when it's time to send a multicast RA.
64  *
65  * @hide
66  */
67 public class RouterAdvertisementDaemon {
68     private static final String TAG = RouterAdvertisementDaemon.class.getSimpleName();
69     private static final byte ICMPV6_ND_ROUTER_SOLICIT = asByte(133);
70     private static final byte ICMPV6_ND_ROUTER_ADVERT  = asByte(134);
71     private static final int IPV6_MIN_MTU = 1280;
72     private static final int MIN_RA_HEADER_SIZE = 16;
73 
74     // Summary of various timers and lifetimes.
75     private static final int MIN_RTR_ADV_INTERVAL_SEC = 300;
76     private static final int MAX_RTR_ADV_INTERVAL_SEC = 600;
77     // In general, router, prefix, and DNS lifetimes are all advised to be
78     // greater than or equal to 3 * MAX_RTR_ADV_INTERVAL.  Here, we double
79     // that to allow for multicast packet loss.
80     //
81     // This MAX_RTR_ADV_INTERVAL_SEC and DEFAULT_LIFETIME are also consistent
82     // with the https://tools.ietf.org/html/rfc7772#section-4 discussion of
83     // "approximately 7 RAs per hour".
84     private static final int DEFAULT_LIFETIME = 6 * MAX_RTR_ADV_INTERVAL_SEC;
85     // From https://tools.ietf.org/html/rfc4861#section-10 .
86     private static final int MIN_DELAY_BETWEEN_RAS_SEC = 3;
87     // Both initial and final RAs, but also for changes in RA contents.
88     // From https://tools.ietf.org/html/rfc4861#section-10 .
89     private static final int  MAX_URGENT_RTR_ADVERTISEMENTS = 5;
90 
91     private static final int DAY_IN_SECONDS = 86_400;
92 
93     private static final byte[] ALL_NODES = new byte[] {
94             (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
95     };
96 
97     private final String mIfName;
98     private final int mIfIndex;
99     private final byte[] mHwAddr;
100     private final InetSocketAddress mAllNodes;
101 
102     // This lock is to protect the RA from being updated while being
103     // transmitted on another thread  (multicast or unicast).
104     //
105     // TODO: This should be handled with a more RCU-like approach.
106     private final Object mLock = new Object();
107     @GuardedBy("mLock")
108     private final byte[] mRA = new byte[IPV6_MIN_MTU];
109     @GuardedBy("mLock")
110     private int mRaLength;
111     @GuardedBy("mLock")
112     private final DeprecatedInfoTracker mDeprecatedInfoTracker;
113     @GuardedBy("mLock")
114     private RaParams mRaParams;
115 
116     private volatile FileDescriptor mSocket;
117     private volatile MulticastTransmitter mMulticastTransmitter;
118     private volatile UnicastResponder mUnicastResponder;
119 
120     public static class RaParams {
121         public boolean hasDefaultRoute;
122         public int mtu;
123         public HashSet<IpPrefix> prefixes;
124         public HashSet<Inet6Address> dnses;
125 
RaParams()126         public RaParams() {
127             hasDefaultRoute = false;
128             mtu = IPV6_MIN_MTU;
129             prefixes = new HashSet<IpPrefix>();
130             dnses = new HashSet<Inet6Address>();
131         }
132 
RaParams(RaParams other)133         public RaParams(RaParams other) {
134             hasDefaultRoute = other.hasDefaultRoute;
135             mtu = other.mtu;
136             prefixes = (HashSet) other.prefixes.clone();
137             dnses = (HashSet) other.dnses.clone();
138         }
139 
140         // Returns the subset of RA parameters that become deprecated when
141         // moving from announcing oldRa to announcing newRa.
142         //
143         // Currently only tracks differences in |prefixes| and |dnses|.
getDeprecatedRaParams(RaParams oldRa, RaParams newRa)144         public static RaParams getDeprecatedRaParams(RaParams oldRa, RaParams newRa) {
145             RaParams newlyDeprecated = new RaParams();
146 
147             if (oldRa != null) {
148                 for (IpPrefix ipp : oldRa.prefixes) {
149                     if (newRa == null || !newRa.prefixes.contains(ipp)) {
150                         newlyDeprecated.prefixes.add(ipp);
151                     }
152                 }
153 
154                 for (Inet6Address dns : oldRa.dnses) {
155                     if (newRa == null || !newRa.dnses.contains(dns)) {
156                         newlyDeprecated.dnses.add(dns);
157                     }
158                 }
159             }
160 
161             return newlyDeprecated;
162         }
163     }
164 
165     private static class DeprecatedInfoTracker {
166         private final HashMap<IpPrefix, Integer> mPrefixes = new HashMap<>();
167         private final HashMap<Inet6Address, Integer> mDnses = new HashMap<>();
168 
getPrefixes()169         Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
170 
putPrefixes(Set<IpPrefix> prefixes)171         void putPrefixes(Set<IpPrefix> prefixes) {
172             for (IpPrefix ipp : prefixes) {
173                 mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
174             }
175         }
176 
removePrefixes(Set<IpPrefix> prefixes)177         void removePrefixes(Set<IpPrefix> prefixes) {
178             for (IpPrefix ipp : prefixes) {
179                 mPrefixes.remove(ipp);
180             }
181         }
182 
getDnses()183         Set<Inet6Address> getDnses() { return mDnses.keySet(); }
184 
putDnses(Set<Inet6Address> dnses)185         void putDnses(Set<Inet6Address> dnses) {
186             for (Inet6Address dns : dnses) {
187                 mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
188             }
189         }
190 
removeDnses(Set<Inet6Address> dnses)191         void removeDnses(Set<Inet6Address> dnses) {
192             for (Inet6Address dns : dnses) {
193                 mDnses.remove(dns);
194             }
195         }
196 
isEmpty()197         boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
198 
decrementCounters()199         private boolean decrementCounters() {
200             boolean removed = decrementCounter(mPrefixes);
201             removed |= decrementCounter(mDnses);
202             return removed;
203         }
204 
decrementCounter(HashMap<T, Integer> map)205         private <T> boolean decrementCounter(HashMap<T, Integer> map) {
206             boolean removed = false;
207 
208             for (Iterator<Map.Entry<T, Integer>> it = map.entrySet().iterator();
209                  it.hasNext();) {
210                 Map.Entry<T, Integer> kv = it.next();
211                 if (kv.getValue() == 0) {
212                     it.remove();
213                     removed = true;
214                 } else {
215                     kv.setValue(kv.getValue() - 1);
216                 }
217             }
218 
219             return removed;
220         }
221     }
222 
223 
RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr)224     public RouterAdvertisementDaemon(String ifname, int ifindex, byte[] hwaddr) {
225         mIfName = ifname;
226         mIfIndex = ifindex;
227         mHwAddr = hwaddr;
228         mAllNodes = new InetSocketAddress(getAllNodesForScopeId(mIfIndex), 0);
229         mDeprecatedInfoTracker = new DeprecatedInfoTracker();
230     }
231 
buildNewRa(RaParams deprecatedParams, RaParams newParams)232     public void buildNewRa(RaParams deprecatedParams, RaParams newParams) {
233         synchronized (mLock) {
234             if (deprecatedParams != null) {
235                 mDeprecatedInfoTracker.putPrefixes(deprecatedParams.prefixes);
236                 mDeprecatedInfoTracker.putDnses(deprecatedParams.dnses);
237             }
238 
239             if (newParams != null) {
240                 // Process information that is no longer deprecated.
241                 mDeprecatedInfoTracker.removePrefixes(newParams.prefixes);
242                 mDeprecatedInfoTracker.removeDnses(newParams.dnses);
243             }
244 
245             mRaParams = newParams;
246             assembleRaLocked();
247         }
248 
249         maybeNotifyMulticastTransmitter();
250     }
251 
start()252     public boolean start() {
253         if (!createSocket()) {
254             return false;
255         }
256 
257         mMulticastTransmitter = new MulticastTransmitter();
258         mMulticastTransmitter.start();
259 
260         mUnicastResponder = new UnicastResponder();
261         mUnicastResponder.start();
262 
263         return true;
264     }
265 
stop()266     public void stop() {
267         closeSocket();
268         mMulticastTransmitter = null;
269         mUnicastResponder = null;
270     }
271 
assembleRaLocked()272     private void assembleRaLocked() {
273         final ByteBuffer ra = ByteBuffer.wrap(mRA);
274         ra.order(ByteOrder.BIG_ENDIAN);
275 
276         boolean shouldSendRA = false;
277 
278         try {
279             putHeader(ra, mRaParams != null && mRaParams.hasDefaultRoute);
280             putSlla(ra, mHwAddr);
281             mRaLength = ra.position();
282 
283             // https://tools.ietf.org/html/rfc5175#section-4 says:
284             //
285             //     "MUST NOT be added to a Router Advertisement message
286             //      if no flags in the option are set."
287             //
288             // putExpandedFlagsOption(ra);
289 
290             if (mRaParams != null) {
291                 putMtu(ra, mRaParams.mtu);
292                 mRaLength = ra.position();
293 
294                 for (IpPrefix ipp : mRaParams.prefixes) {
295                     putPio(ra, ipp, DEFAULT_LIFETIME, DEFAULT_LIFETIME);
296                     mRaLength = ra.position();
297                     shouldSendRA = true;
298                 }
299 
300                 if (mRaParams.dnses.size() > 0) {
301                     putRdnss(ra, mRaParams.dnses, DEFAULT_LIFETIME);
302                     mRaLength = ra.position();
303                     shouldSendRA = true;
304                 }
305             }
306 
307             for (IpPrefix ipp : mDeprecatedInfoTracker.getPrefixes()) {
308                 putPio(ra, ipp, 0, 0);
309                 mRaLength = ra.position();
310                 shouldSendRA = true;
311             }
312 
313             final Set<Inet6Address> deprecatedDnses = mDeprecatedInfoTracker.getDnses();
314             if (!deprecatedDnses.isEmpty()) {
315                 putRdnss(ra, deprecatedDnses, 0);
316                 mRaLength = ra.position();
317                 shouldSendRA = true;
318             }
319         } catch (BufferOverflowException e) {
320             // The packet up to mRaLength  is valid, since it has been updated
321             // progressively as the RA was built. Log an error, and continue
322             // on as best as possible.
323             Log.e(TAG, "Could not construct new RA: " + e);
324         }
325 
326         // We have nothing worth announcing; indicate as much to maybeSendRA().
327         if (!shouldSendRA) {
328             mRaLength = 0;
329         }
330     }
331 
maybeNotifyMulticastTransmitter()332     private void maybeNotifyMulticastTransmitter() {
333         final MulticastTransmitter m = mMulticastTransmitter;
334         if (m != null) {
335             m.hup();
336         }
337     }
338 
getAllNodesForScopeId(int scopeId)339     private static Inet6Address getAllNodesForScopeId(int scopeId) {
340         try {
341             return Inet6Address.getByAddress("ff02::1", ALL_NODES, scopeId);
342         } catch (UnknownHostException uhe) {
343             Log.wtf(TAG, "Failed to construct ff02::1 InetAddress: " + uhe);
344             return null;
345         }
346     }
347 
asByte(int value)348     private static byte asByte(int value) { return (byte) value; }
asShort(int value)349     private static short asShort(int value) { return (short) value; }
350 
putHeader(ByteBuffer ra, boolean hasDefaultRoute)351     private static void putHeader(ByteBuffer ra, boolean hasDefaultRoute) {
352         /**
353             Router Advertisement Message Format
354 
355              0                   1                   2                   3
356              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
357             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
358             |     Type      |     Code      |          Checksum             |
359             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
360             | Cur Hop Limit |M|O|H|Prf|P|R|R|       Router Lifetime         |
361             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
362             |                         Reachable Time                        |
363             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
364             |                          Retrans Timer                        |
365             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
366             |   Options ...
367             +-+-+-+-+-+-+-+-+-+-+-+-
368         */
369         final byte DEFAULT_HOPLIMIT = 64;
370         ra.put(ICMPV6_ND_ROUTER_ADVERT)
371           .put(asByte(0))
372           .putShort(asShort(0))
373           .put(DEFAULT_HOPLIMIT)
374           // RFC 4191 "high" preference, iff. advertising a default route.
375           .put(hasDefaultRoute ? asByte(0x08) : asByte(0))
376           .putShort(hasDefaultRoute ? asShort(DEFAULT_LIFETIME) : asShort(0))
377           .putInt(0)
378           .putInt(0);
379     }
380 
putSlla(ByteBuffer ra, byte[] slla)381     private static void putSlla(ByteBuffer ra, byte[] slla) {
382         /**
383             Source/Target Link-layer Address
384 
385              0                   1                   2                   3
386              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
387             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
388             |     Type      |    Length     |    Link-Layer Address ...
389             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
390         */
391         if (slla == null || slla.length != 6) {
392             // Only IEEE 802.3 6-byte addresses are supported.
393             return;
394         }
395         final byte ND_OPTION_SLLA = 1;
396         final byte SLLA_NUM_8OCTETS = 1;
397         ra.put(ND_OPTION_SLLA)
398           .put(SLLA_NUM_8OCTETS)
399           .put(slla);
400     }
401 
putExpandedFlagsOption(ByteBuffer ra)402     private static void putExpandedFlagsOption(ByteBuffer ra) {
403         /**
404             Router Advertisement Expanded Flags Option
405 
406              0                   1                   2                   3
407              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
408             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
409             |     Type      |    Length     |         Bit fields available ..
410             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
411             ... for assignment                                              |
412             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
413          */
414 
415         final byte ND_OPTION_EFO = 26;
416         final byte EFO_NUM_8OCTETS = 1;
417 
418         ra.put(ND_OPTION_EFO)
419           .put(EFO_NUM_8OCTETS)
420           .putShort(asShort(0))
421           .putInt(0);
422     }
423 
putMtu(ByteBuffer ra, int mtu)424     private static void putMtu(ByteBuffer ra, int mtu) {
425         /**
426             MTU
427 
428              0                   1                   2                   3
429              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
430             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
431             |     Type      |    Length     |           Reserved            |
432             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
433             |                              MTU                              |
434             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
435         */
436         final byte ND_OPTION_MTU = 5;
437         final byte MTU_NUM_8OCTETS = 1;
438         ra.put(ND_OPTION_MTU)
439           .put(MTU_NUM_8OCTETS)
440           .putShort(asShort(0))
441           .putInt((mtu < IPV6_MIN_MTU) ? IPV6_MIN_MTU : mtu);
442     }
443 
444     private static void putPio(ByteBuffer ra, IpPrefix ipp,
445                                int validTime, int preferredTime) {
446         /**
447             Prefix Information
448 
449              0                   1                   2                   3
450              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
451             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
452             |     Type      |    Length     | Prefix Length |L|A| Reserved1 |
453             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
454             |                         Valid Lifetime                        |
455             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
456             |                       Preferred Lifetime                      |
457             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
458             |                           Reserved2                           |
459             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
460             |                                                               |
461             +                                                               +
462             |                                                               |
463             +                            Prefix                             +
464             |                                                               |
465             +                                                               +
466             |                                                               |
467             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
468         */
469         final int prefixLength = ipp.getPrefixLength();
470         if (prefixLength != 64) {
471             return;
472         }
473         final byte ND_OPTION_PIO = 3;
474         final byte PIO_NUM_8OCTETS = 4;
475 
476         if (validTime < 0) validTime = 0;
477         if (preferredTime < 0) preferredTime = 0;
478         if (preferredTime > validTime) preferredTime = validTime;
479 
480         final byte[] addr = ipp.getAddress().getAddress();
481         ra.put(ND_OPTION_PIO)
482           .put(PIO_NUM_8OCTETS)
483           .put(asByte(prefixLength))
484           .put(asByte(0xc0)) /* L & A set */
485           .putInt(validTime)
486           .putInt(preferredTime)
487           .putInt(0)
488           .put(addr);
489     }
490 
491     private static void putRio(ByteBuffer ra, IpPrefix ipp) {
492         /**
493             Route Information Option
494 
495              0                   1                   2                   3
496              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
497             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
498             |     Type      |    Length     | Prefix Length |Resvd|Prf|Resvd|
499             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
500             |                        Route Lifetime                         |
501             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
502             |                   Prefix (Variable Length)                    |
503             .                                                               .
504             .                                                               .
505             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
506          */
507         final int prefixLength = ipp.getPrefixLength();
508         if (prefixLength > 64) {
509             return;
510         }
511         final byte ND_OPTION_RIO = 24;
512         final byte RIO_NUM_8OCTETS = asByte(
513                 (prefixLength == 0) ? 1 : (prefixLength <= 8) ? 2 : 3);
514 
515         final byte[] addr = ipp.getAddress().getAddress();
516         ra.put(ND_OPTION_RIO)
517           .put(RIO_NUM_8OCTETS)
518           .put(asByte(prefixLength))
519           .put(asByte(0x18))
520           .putInt(DEFAULT_LIFETIME);
521 
522         // Rely upon an IpPrefix's address being properly zeroed.
523         if (prefixLength > 0) {
524             ra.put(addr, 0, (prefixLength <= 64) ? 8 : 16);
525         }
526     }
527 
528     private static void putRdnss(ByteBuffer ra, Set<Inet6Address> dnses, int lifetime) {
529         /**
530             Recursive DNS Server (RDNSS) Option
531 
532              0                   1                   2                   3
533              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
534             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
535             |     Type      |     Length    |           Reserved            |
536             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
537             |                           Lifetime                            |
538             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
539             |                                                               |
540             :            Addresses of IPv6 Recursive DNS Servers            :
541             |                                                               |
542             +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
543          */
544 
545         final byte ND_OPTION_RDNSS = 25;
546         final byte RDNSS_NUM_8OCTETS = asByte(dnses.size() * 2 + 1);
547         ra.put(ND_OPTION_RDNSS)
548           .put(RDNSS_NUM_8OCTETS)
549           .putShort(asShort(0))
550           .putInt(lifetime);
551 
552         for (Inet6Address dns : dnses) {
553             // NOTE: If the full of list DNS servers doesn't fit in the packet,
554             // this code will cause a buffer overflow and the RA won't include
555             // this instance of the option at all.
556             //
557             // TODO: Consider looking at ra.remaining() to determine how many
558             // DNS servers will fit, and adding only those.
559             ra.put(dns.getAddress());
560         }
561     }
562 
563     private boolean createSocket() {
564         final int SEND_TIMEOUT_MS = 300;
565 
566         try {
567             mSocket = Os.socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
568             // Setting SNDTIMEO is purely for defensive purposes.
569             Os.setsockoptTimeval(
570                     mSocket, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(SEND_TIMEOUT_MS));
571             Os.setsockoptIfreq(mSocket, SOL_SOCKET, SO_BINDTODEVICE, mIfName);
572             NetworkUtils.protectFromVpn(mSocket);
573             NetworkUtils.setupRaSocket(mSocket, mIfIndex);
574         } catch (ErrnoException | IOException e) {
575             Log.e(TAG, "Failed to create RA daemon socket: " + e);
576             return false;
577         }
578 
579         return true;
580     }
581 
582     private void closeSocket() {
583         if (mSocket != null) {
584             try {
585                 IoBridge.closeAndSignalBlockedThreads(mSocket);
586             } catch (IOException ignored) {}
587         }
588         mSocket = null;
589     }
590 
591     private boolean isSocketValid() {
592         final FileDescriptor s = mSocket;
593         return (s != null) && s.valid();
594     }
595 
596     private boolean isSuitableDestination(InetSocketAddress dest) {
597         if (mAllNodes.equals(dest)) {
598             return true;
599         }
600 
601         final InetAddress destip = dest.getAddress();
602         return (destip instanceof Inet6Address) &&
603                 destip.isLinkLocalAddress() &&
604                (((Inet6Address) destip).getScopeId() == mIfIndex);
605     }
606 
607     private void maybeSendRA(InetSocketAddress dest) {
608         if (dest == null || !isSuitableDestination(dest)) {
609             dest = mAllNodes;
610         }
611 
612         try {
613             synchronized (mLock) {
614                 if (mRaLength < MIN_RA_HEADER_SIZE) {
615                     // No actual RA to send.
616                     return;
617                 }
618                 Os.sendto(mSocket, mRA, 0, mRaLength, 0, dest);
619             }
620             Log.d(TAG, "RA sendto " + dest.getAddress().getHostAddress());
621         } catch (ErrnoException | SocketException e) {
622             if (isSocketValid()) {
623                 Log.e(TAG, "sendto error: " + e);
624             }
625         }
626     }
627 
628     private final class UnicastResponder extends Thread {
629         private final InetSocketAddress solicitor = new InetSocketAddress();
630         // The recycled buffer for receiving Router Solicitations from clients.
631         // If the RS is larger than IPV6_MIN_MTU the packets are truncated.
632         // This is fine since currently only byte 0 is examined anyway.
633         private final byte mSolication[] = new byte[IPV6_MIN_MTU];
634 
635         @Override
636         public void run() {
637             while (isSocketValid()) {
638                 try {
639                     // Blocking receive.
640                     final int rval = Os.recvfrom(
641                             mSocket, mSolication, 0, mSolication.length, 0, solicitor);
642                     // Do the least possible amount of validation.
643                     if (rval < 1 || mSolication[0] != ICMPV6_ND_ROUTER_SOLICIT) {
644                         continue;
645                     }
646                 } catch (ErrnoException | SocketException e) {
647                     if (isSocketValid()) {
648                         Log.e(TAG, "recvfrom error: " + e);
649                     }
650                     continue;
651                 }
652 
653                 maybeSendRA(solicitor);
654             }
655         }
656     }
657 
658     // TODO: Consider moving this to run on a provided Looper as a Handler,
659     // with WakeupMessage-style messages providing the timer driven input.
660     private final class MulticastTransmitter extends Thread {
661         private final Random mRandom = new Random();
662         private final AtomicInteger mUrgentAnnouncements = new AtomicInteger(0);
663 
664         @Override
665         public void run() {
666             while (isSocketValid()) {
667                 try {
668                     Thread.sleep(getNextMulticastTransmitDelayMs());
669                 } catch (InterruptedException ignored) {
670                     // Stop sleeping, immediately send an RA, and continue.
671                 }
672 
673                 maybeSendRA(mAllNodes);
674                 synchronized (mLock) {
675                     if (mDeprecatedInfoTracker.decrementCounters()) {
676                         // At least one deprecated PIO has been removed;
677                         // reassemble the RA.
678                         assembleRaLocked();
679                     }
680                 }
681             }
682         }
683 
684         public void hup() {
685             // Set to one fewer that the desired number, because as soon as
686             // the thread interrupt is processed we immediately send an RA
687             // and mUrgentAnnouncements is not examined until the subsequent
688             // sleep interval computation (i.e. this way we send 3 and not 4).
689             mUrgentAnnouncements.set(MAX_URGENT_RTR_ADVERTISEMENTS - 1);
690             interrupt();
691         }
692 
693         private int getNextMulticastTransmitDelaySec() {
694             boolean deprecationInProgress = false;
695             synchronized (mLock) {
696                 if (mRaLength < MIN_RA_HEADER_SIZE) {
697                     // No actual RA to send; just sleep for 1 day.
698                     return DAY_IN_SECONDS;
699                 }
700                 deprecationInProgress = !mDeprecatedInfoTracker.isEmpty();
701             }
702 
703             final int urgentPending = mUrgentAnnouncements.getAndDecrement();
704             if ((urgentPending > 0) || deprecationInProgress) {
705                 return MIN_DELAY_BETWEEN_RAS_SEC;
706             }
707 
708             return MIN_RTR_ADV_INTERVAL_SEC + mRandom.nextInt(
709                     MAX_RTR_ADV_INTERVAL_SEC - MIN_RTR_ADV_INTERVAL_SEC);
710         }
711 
712         private long getNextMulticastTransmitDelayMs() {
713             return 1000 * (long) getNextMulticastTransmitDelaySec();
714         }
715     }
716 }
717