Home | History | Annotate | Download | only in ip
      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 
    126         public RaParams() {
    127             hasDefaultRoute = false;
    128             mtu = IPV6_MIN_MTU;
    129             prefixes = new HashSet<IpPrefix>();
    130             dnses = new HashSet<Inet6Address>();
    131         }
    132 
    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|.
    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 
    169         Set<IpPrefix> getPrefixes() { return mPrefixes.keySet(); }
    170 
    171         void putPrefixes(Set<IpPrefix> prefixes) {
    172             for (IpPrefix ipp : prefixes) {
    173                 mPrefixes.put(ipp, MAX_URGENT_RTR_ADVERTISEMENTS);
    174             }
    175         }
    176 
    177         void removePrefixes(Set<IpPrefix> prefixes) {
    178             for (IpPrefix ipp : prefixes) {
    179                 mPrefixes.remove(ipp);
    180             }
    181         }
    182 
    183         Set<Inet6Address> getDnses() { return mDnses.keySet(); }
    184 
    185         void putDnses(Set<Inet6Address> dnses) {
    186             for (Inet6Address dns : dnses) {
    187                 mDnses.put(dns, MAX_URGENT_RTR_ADVERTISEMENTS);
    188             }
    189         }
    190 
    191         void removeDnses(Set<Inet6Address> dnses) {
    192             for (Inet6Address dns : dnses) {
    193                 mDnses.remove(dns);
    194             }
    195         }
    196 
    197         boolean isEmpty() { return mPrefixes.isEmpty() && mDnses.isEmpty(); }
    198 
    199         private boolean decrementCounters() {
    200             boolean removed = decrementCounter(mPrefixes);
    201             removed |= decrementCounter(mDnses);
    202             return removed;
    203         }
    204 
    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 
    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 
    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 
    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 
    266     public void stop() {
    267         closeSocket();
    268         mMulticastTransmitter = null;
    269         mUnicastResponder = null;
    270     }
    271 
    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 
    332     private void maybeNotifyMulticastTransmitter() {
    333         final MulticastTransmitter m = mMulticastTransmitter;
    334         if (m != null) {
    335             m.hup();
    336         }
    337     }
    338 
    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 
    348     private static byte asByte(int value) { return (byte) value; }
    349     private static short asShort(int value) { return (short) value; }
    350 
    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 
    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 
    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 
    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