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