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.util.Log;
     28 
     29 import java.net.Inet6Address;
     30 import java.net.InetAddress;
     31 import java.util.ArrayList;
     32 import java.util.LinkedList;
     33 
     34 
     35 /**
     36  * IPv6 tethering is rather different from IPv4 owing to the absence of NAT.
     37  * This coordinator is responsible for evaluating the dedicated prefixes
     38  * assigned to the device and deciding how to divvy them up among downstream
     39  * interfaces.
     40  *
     41  * @hide
     42  */
     43 public class IPv6TetheringCoordinator {
     44     private static final String TAG = IPv6TetheringCoordinator.class.getSimpleName();
     45     private static final boolean DBG = false;
     46     private static final boolean VDBG = false;
     47 
     48     private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
     49     private final LinkedList<TetherInterfaceStateMachine> mActiveDownstreams;
     50     private NetworkState mUpstreamNetworkState;
     51 
     52     public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
     53         mNotifyList = notifyList;
     54         mActiveDownstreams = new LinkedList<>();
     55     }
     56 
     57     public void addActiveDownstream(TetherInterfaceStateMachine downstream) {
     58         if (mActiveDownstreams.indexOf(downstream) == -1) {
     59             // Adding a new downstream appends it to the list. Adding a
     60             // downstream a second time without first removing it has no effect.
     61             mActiveDownstreams.offer(downstream);
     62             updateIPv6TetheringInterfaces();
     63         }
     64     }
     65 
     66     public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
     67         stopIPv6TetheringOn(downstream);
     68         if (mActiveDownstreams.remove(downstream)) {
     69             updateIPv6TetheringInterfaces();
     70         }
     71     }
     72 
     73     public void updateUpstreamNetworkState(NetworkState ns) {
     74         if (VDBG) {
     75             Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns));
     76         }
     77         if (!canTetherIPv6(ns)) {
     78             stopIPv6TetheringOnAllInterfaces();
     79             setUpstreamNetworkState(null);
     80             return;
     81         }
     82 
     83         if (mUpstreamNetworkState != null &&
     84             !ns.network.equals(mUpstreamNetworkState.network)) {
     85             stopIPv6TetheringOnAllInterfaces();
     86         }
     87 
     88         setUpstreamNetworkState(ns);
     89         updateIPv6TetheringInterfaces();
     90     }
     91 
     92     private void stopIPv6TetheringOnAllInterfaces() {
     93         for (TetherInterfaceStateMachine sm : mNotifyList) {
     94             stopIPv6TetheringOn(sm);
     95         }
     96     }
     97 
     98     private void setUpstreamNetworkState(NetworkState ns) {
     99         if (ns == null) {
    100             mUpstreamNetworkState = null;
    101         } else {
    102             // Make a deep copy of the parts we need.
    103             mUpstreamNetworkState = new NetworkState(
    104                     null,
    105                     new LinkProperties(ns.linkProperties),
    106                     new NetworkCapabilities(ns.networkCapabilities),
    107                     new Network(ns.network),
    108                     null,
    109                     null);
    110         }
    111 
    112         if (DBG) {
    113             Log.d(TAG, "setUpstreamNetworkState: " + toDebugString(mUpstreamNetworkState));
    114         }
    115     }
    116 
    117     private void updateIPv6TetheringInterfaces() {
    118         for (TetherInterfaceStateMachine sm : mNotifyList) {
    119             final LinkProperties lp = getInterfaceIPv6LinkProperties(sm);
    120             sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, lp);
    121             break;
    122         }
    123     }
    124 
    125     private LinkProperties getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm) {
    126         if (mUpstreamNetworkState == null) return null;
    127 
    128         if (sm.interfaceType() == ConnectivityManager.TETHERING_BLUETOOTH) {
    129             // TODO: Figure out IPv6 support on PAN interfaces.
    130             return null;
    131         }
    132 
    133         // NOTE: Here, in future, we would have policies to decide how to divvy
    134         // up the available dedicated prefixes among downstream interfaces.
    135         // At this time we have no such mechanism--we only support tethering
    136         // IPv6 toward the oldest (first requested) active downstream.
    137 
    138         final TetherInterfaceStateMachine currentActive = mActiveDownstreams.peek();
    139         if (currentActive != null && currentActive == sm) {
    140             final LinkProperties lp = getIPv6OnlyLinkProperties(
    141                     mUpstreamNetworkState.linkProperties);
    142             if (lp.hasIPv6DefaultRoute() && lp.hasGlobalIPv6Address()) {
    143                 return lp;
    144             }
    145         }
    146 
    147         return null;
    148     }
    149 
    150     private static boolean canTetherIPv6(NetworkState ns) {
    151         // Broadly speaking:
    152         //
    153         //     [1] does the upstream have an IPv6 default route?
    154         //
    155         // and
    156         //
    157         //     [2] does the upstream have one or more global IPv6 /64s
    158         //         dedicated to this device?
    159         //
    160         // In lieu of Prefix Delegation and other evaluation of whether a
    161         // prefix may or may not be dedicated to this device, for now just
    162         // check whether the upstream is TRANSPORT_CELLULAR. This works
    163         // because "[t]he 3GPP network allocates each default bearer a unique
    164         // /64 prefix", per RFC 6459, Section 5.2.
    165 
    166         final boolean canTether =
    167                 (ns != null) && (ns.network != null) &&
    168                 (ns.linkProperties != null) && (ns.networkCapabilities != null) &&
    169                 // At least one upstream DNS server:
    170                 ns.linkProperties.isProvisioned() &&
    171                 // Minimal amount of IPv6 provisioning:
    172                 ns.linkProperties.hasIPv6DefaultRoute() &&
    173                 ns.linkProperties.hasGlobalIPv6Address() &&
    174                 // Temporary approximation of "dedicated prefix":
    175                 ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
    176 
    177         // For now, we do not support separate IPv4 and IPv6 upstreams (e.g.
    178         // tethering with 464xlat involved). TODO: Rectify this shortcoming,
    179         // likely by calling NetworkManagementService#startInterfaceForwarding()
    180         // for all upstream interfaces.
    181         RouteInfo v4default = null;
    182         RouteInfo v6default = null;
    183         if (canTether) {
    184             for (RouteInfo r : ns.linkProperties.getAllRoutes()) {
    185                 if (r.isIPv4Default()) {
    186                     v4default = r;
    187                 } else if (r.isIPv6Default()) {
    188                     v6default = r;
    189                 }
    190 
    191                 if (v4default != null && v6default != null) {
    192                     break;
    193                 }
    194             }
    195         }
    196 
    197         final boolean supportedConfiguration =
    198                 (v4default != null) && (v6default != null) &&
    199                 (v4default.getInterface() != null) &&
    200                 v4default.getInterface().equals(v6default.getInterface());
    201 
    202         final boolean outcome = canTether && supportedConfiguration;
    203 
    204         if (VDBG) {
    205             if (ns == null) {
    206                 Log.d(TAG, "No available upstream.");
    207             } else {
    208                 Log.d(TAG, String.format("IPv6 tethering is %s for upstream: %s",
    209                         (outcome ? "available" : "not available"), toDebugString(ns)));
    210             }
    211         }
    212 
    213         return outcome;
    214     }
    215 
    216     private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) {
    217         final LinkProperties v6only = new LinkProperties();
    218         if (lp == null) {
    219             return v6only;
    220         }
    221 
    222         // NOTE: At this time we don't copy over any information about any
    223         // stacked links. No current stacked link configuration has IPv6.
    224 
    225         v6only.setInterfaceName(lp.getInterfaceName());
    226 
    227         v6only.setMtu(lp.getMtu());
    228 
    229         for (LinkAddress linkAddr : lp.getLinkAddresses()) {
    230             if (linkAddr.isGlobalPreferred() && linkAddr.getPrefixLength() == 64) {
    231                 v6only.addLinkAddress(linkAddr);
    232             }
    233         }
    234 
    235         for (RouteInfo routeInfo : lp.getRoutes()) {
    236             final IpPrefix destination = routeInfo.getDestination();
    237             if ((destination.getAddress() instanceof Inet6Address) &&
    238                 (destination.getPrefixLength() <= 64)) {
    239                 v6only.addRoute(routeInfo);
    240             }
    241         }
    242 
    243         for (InetAddress dnsServer : lp.getDnsServers()) {
    244             if (isIPv6GlobalAddress(dnsServer)) {
    245                 // For now we include ULAs.
    246                 v6only.addDnsServer(dnsServer);
    247             }
    248         }
    249 
    250         v6only.setDomains(lp.getDomains());
    251 
    252         return v6only;
    253     }
    254 
    255     // TODO: Delete this and switch to LinkAddress#isGlobalPreferred once we
    256     // announce our own IPv6 address as DNS server.
    257     private static boolean isIPv6GlobalAddress(InetAddress ip) {
    258         return (ip instanceof Inet6Address) &&
    259                !ip.isAnyLocalAddress() &&
    260                !ip.isLoopbackAddress() &&
    261                !ip.isLinkLocalAddress() &&
    262                !ip.isSiteLocalAddress() &&
    263                !ip.isMulticastAddress();
    264     }
    265 
    266     private static String toDebugString(NetworkState ns) {
    267         if (ns == null) {
    268             return "NetworkState{null}";
    269         }
    270         return String.format("NetworkState{%s, %s, %s}",
    271                 ns.network,
    272                 ns.networkCapabilities,
    273                 ns.linkProperties);
    274     }
    275 
    276     private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
    277         sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
    278     }
    279 }
    280