Home | History | Annotate | Download | only in tethering
      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 com.android.server.connectivity.tethering;
     18 
     19 import android.net.ConnectivityManager;
     20 import android.net.IpPrefix;
     21 import android.net.LinkAddress;
     22 import android.net.LinkProperties;
     23 import android.net.Network;
     24 import android.net.NetworkCapabilities;
     25 import android.net.NetworkState;
     26 import android.net.RouteInfo;
     27 import android.net.util.NetworkConstants;
     28 import android.net.util.SharedLog;
     29 import android.util.Log;
     30 
     31 import java.net.Inet6Address;
     32 import java.net.InetAddress;
     33 import java.util.ArrayList;
     34 import java.util.Arrays;
     35 import java.util.LinkedList;
     36 import java.util.Random;
     37 
     38 
     39 /**
     40  * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
     41  * This coordinator is responsible for evaluating the dedicated prefixes
     42  * assigned to the device and deciding how to divvy them up among downstream
     43  * interfaces.
     44  *
     45  * @hide
     46  */
     47 public class IPv6TetheringCoordinator {
     48     private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
     49     private static final boolean DBG = false;
     50     private static final boolean VDBG = false;
     51 
     52     private static class Downstream {
     53         public final TetherInterfaceStateMachine tism;
     54         public final int mode;  // IControlsTethering.STATE_*
     55         // Used to append to a ULA /48, constructing a ULA /64 for local use.
     56         public final short subnetId;
     57 
     58         Downstream(TetherInterfaceStateMachine tism, int mode, short subnetId) {
     59             this.tism = tism;
     60             this.mode = mode;
     61             this.subnetId = subnetId;
     62         }
     63     }
     64 
     65     private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
     66     private final SharedLog mLog;
     67     // NOTE: mActiveDownstreams is a list and not a hash data structure because
     68     // we keep active downstreams in arrival order.  This is done so /64s can
     69     // be parceled out on a "first come, first served" basis and a /64 used by
     70     // a downstream that is no longer active can be redistributed to any next
     71     // waiting active downstream (again, in arrival order).
     72     private final LinkedList<Downstream> mActiveDownstreams;
     73     private final byte[] mUniqueLocalPrefix;
     74     private short mNextSubnetId;
     75     private NetworkState mUpstreamNetworkState;
     76 
     77     public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList,
     78                                     SharedLog log) {
     79         mNotifyList = notifyList;
     80         mLog = log.forSubComponent(TAG);
     81         mActiveDownstreams = new LinkedList<>();
     82         mUniqueLocalPrefix = generateUniqueLocalPrefix();
     83         mNextSubnetId = 0;
     84     }
     85 
     86     public void addActiveDownstream(TetherInterfaceStateMachine downstream, int mode) {
     87         if (findDownstream(downstream) == null) {
     88             // Adding a new downstream appends it to the list. Adding a
     89             // downstream a second time without first removing it has no effect.
     90             // We never change the mode of a downstream except by first removing
     91             // it and then re-adding it (with its new mode specified);
     92             if (mActiveDownstreams.offer(new Downstream(downstream, mode, mNextSubnetId))) {
     93                 // Make sure subnet IDs are always positive. They are appended
     94                 // to a ULA /48 to make a ULA /64 for local use.
     95                 mNextSubnetId = (short) Math.max(0, mNextSubnetId + 1);
     96             }
     97             updateIPv6TetheringInterfaces();
     98         }
     99     }
    100 
    101     public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
    102         stopIPv6TetheringOn(downstream);
    103         if (mActiveDownstreams.remove(findDownstream(downstream))) {
    104             updateIPv6TetheringInterfaces();
    105         }
    106 
    107         // When tethering is stopping we can reset the subnet counter.
    108         if (mNotifyList.isEmpty()) {
    109             if (!mActiveDownstreams.isEmpty()) {
    110                 Log.wtf(TAG, "Tethering notify list empty, IPv6 downstreams non-empty.");
    111             }
    112             mNextSubnetId = 0;
    113         }
    114     }
    115 
    116     public void updateUpstreamNetworkState(NetworkState ns) {
    117         if (VDBG) {
    118             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
    119         }
    120         if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) {
    121             stopIPv6TetheringOnAllInterfaces();
    122             setUpstreamNetworkState(null);
    123             return;
    124         }
    125 
    126         if (mUpstreamNetworkState != null &&
    127             !ns.network.equals(mUpstreamNetworkState.network)) {
    128             stopIPv6TetheringOnAllInterfaces();
    129         }
    130 
    131         setUpstreamNetworkState(ns);
    132         updateIPv6TetheringInterfaces();
    133     }
    134 
    135     private void stopIPv6TetheringOnAllInterfaces() {
    136         for (TetherInterfaceStateMachine sm : mNotifyList) {
    137             stopIPv6TetheringOn(sm);
    138         }
    139     }
    140 
    141     private void setUpstreamNetworkState(NetworkState ns) {
    142         if (ns == null) {
    143             mUpstreamNetworkState = null;
    144         } else {
    145             // Make a deep copy of the parts we need.
    146             mUpstreamNetworkState = new NetworkState(
    147                     null,
    148                     new LinkProperties(ns.linkProperties),
    149                     new NetworkCapabilities(ns.networkCapabilities),
    150                     new Network(ns.network),
    151                     null,
    152                     null);
    153         }
    154 
    155         mLog.log("setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
    156     }
    157 
    158     private void updateIPv6TetheringInterfaces() {
    159         for (TetherInterfaceStateMachine sm : mNotifyList) {
    160             final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
    161             sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
    162             break;
    163         }
    164     }
    165 
    166     private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
    167         if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
    168             // TODO: Figure out IPv6 support on PAN interfaces.
    169             return null;
    170         }
    171 
    172         final Downstream ds = findDownstream(sm);
    173         if (ds == null) return null;
    174 
    175         if (ds.mode == IControlsTethering.STATE_LOCAL_ONLY) {
    176             // Build a Unique Locally-assigned Prefix configuration.
    177             return getUniqueLocalConfig(mUniqueLocalPrefix, ds.subnetId);
    178         }
    179 
    180         // This downstream is in IControlsTethering.STATE_TETHERED mode.
    181         if (mUpstreamNetworkState == null || mUpstreamNetworkState.linkProperties == null) {
    182             return null;
    183         }
    184 
    185         // NOTE: Here, in future, we would have policies to decide how to divvy
    186         // up the available dedicated prefixes among downstream interfaces.
    187         // At this time we have no such mechanism--we only support tethering
    188         // IPv6 toward the oldest (first requested) active downstream.
    189 
    190         final Downstream currentActive = mActiveDownstreams.peek();
    191         if (currentActive != null && currentActive.tism == sm) {
    192             final LinkProperties lp = getIPv6OnlyLinkProperties(
    193                     mUpstreamNetworkState.linkProperties);
    194             if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
    195                 return lp;
    196             }
    197         }
    198 
    199         return null;
    200     }
    201 
    202     Downstream findDownstream(TetherInterfaceStateMachine tism) {
    203         for (Downstream ds : mActiveDownstreams) {
    204             if (ds.tism == tism) return ds;
    205         }
    206         return null;
    207     }
    208 
    209     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
    210         final LinkProperties v6only = new LinkProperties();
    211         if (lp == null) {
    212             return v6only;
    213         }
    214 
    215         // NOTE: At this time we don't copy over any information about any
    216         // stacked links. No current stacked link configuration has IPv6.
    217 
    218         v6only.setInterfaceName(lp.getInterfaceName());
    219 
    220         v6only.setMtu(lp.getMtu());
    221 
    222         for (LinkAddress linkAddr : lp.getLinkAddresses()) {
    223             if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
    224                 v6only.addLinkAddress(linkAddr);
    225             }
    226         }
    227 
    228         for (RouteInfo routeInfo : lp.getRoutes()) {
    229             final IpPrefix destination = routeInfo.getDestination();
    230             if ((destination.getAddress() instanceof Inet6Address) &&
    231                 (destination.getPrefixLength() <= 64)) {
    232                 v6only.addRoute(routeInfo);
    233             }
    234         }
    235 
    236         for (InetAddress dnsServer : lp.getDnsServers()) {
    237             if (isIPv6GlobalAddress(dnsServer)) {
    238                 // For now we include ULAs.
    239                 v6only.addDnsServer(dnsServer);
    240             }
    241         }
    242 
    243         v6only.setDomains(lp.getDomains());
    244 
    245         return v6only;
    246     }
    247 
    248     // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
    249     // announce our own IPv6 address as DNS server.
    250     private static boolean isIPv6GlobalAddress(InetAddress ip) {
    251         return (ip instanceof Inet6Address) &&
    252                !ip.isAnyLocalAddress() &&
    253                !ip.isLoopbackAddress() &&
    254                !ip.isLinkLocalAddress() &&
    255                !ip.isSiteLocalAddress() &&
    256                !ip.isMulticastAddress();
    257     }
    258 
    259     private static LinkProperties getUniqueLocalConfig(byte[] ulp, short subnetId) {
    260         final LinkProperties lp = new LinkProperties();
    261 
    262         final IpPrefix local48 = makeUniqueLocalPrefix(ulp, (short) 0, 48);
    263         lp.addRoute(new RouteInfo(local48, null, null));
    264 
    265         final IpPrefix local64 = makeUniqueLocalPrefix(ulp, subnetId, 64);
    266         // Because this is a locally-generated ULA, we don't have an upstream
    267         // address. But because the downstream IP address management code gets
    268         // its prefix from the upstream's IP address, we create a fake one here.
    269         lp.addLinkAddress(new LinkAddress(local64.getAddress(), 64));
    270 
    271         lp.setMtu(NetworkConstants.ETHER_MTU);
    272         return lp;
    273     }
    274 
    275     private static IpPrefix makeUniqueLocalPrefix(byte[] in6addr, short subnetId, int prefixlen) {
    276         final byte[] bytes = Arrays.copyOf(in6addr, in6addr.length);
    277         bytes[7] = (byte) (subnetId >> 8);
    278         bytes[8] = (byte) subnetId;
    279         return new IpPrefix(bytes, prefixlen);
    280     }
    281 
    282     // Generates a Unique Locally-assigned Prefix:
    283     //
    284     //     https://tools.ietf.org/html/rfc4193#section-3.1
    285     //
    286     // The result is a /48 that can be used for local-only communications.
    287     private static byte[] generateUniqueLocalPrefix() {
    288         final byte[] ulp = new byte[6];  // 6 = 48bits / 8bits/byte
    289         (new Random()).nextBytes(ulp);
    290 
    291         final byte[] in6addr = Arrays.copyOf(ulp, NetworkConstants.IPV6_ADDR_LEN);
    292         in6addr[0] = (byte) 0xfd;  // fc00::/7 and L=1
    293 
    294         return in6addr;
    295     }
    296 
    297     private static String toDebugString(NetworkState ns) {
    298         if (ns == null) {
    299             return "NetworkState{null}";
    300         }
    301         return String.format("NetworkState{%s, %s, %s}",
    302                 ns.network,
    303                 ns.networkCapabilities,
    304                 ns.linkProperties);
    305     }
    306 
    307     private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
    308         sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
    309     }
    310 }
    311