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.INetd;
     20 import android.net.IpPrefix;
     21 import android.net.LinkAddress;
     22 import android.net.LinkProperties;
     23 import android.net.NetworkCapabilities;
     24 import android.net.NetworkState;
     25 import android.net.RouteInfo;
     26 import android.net.ip.RouterAdvertisementDaemon;
     27 import android.net.ip.RouterAdvertisementDaemon.RaParams;
     28 import android.os.INetworkManagementService;
     29 import android.os.ServiceSpecificException;
     30 import android.os.RemoteException;
     31 import android.util.Log;
     32 import android.util.Slog;
     33 
     34 import java.net.Inet6Address;
     35 import java.net.InetAddress;
     36 import java.net.NetworkInterface;
     37 import java.net.SocketException;
     38 import java.net.UnknownHostException;
     39 import java.util.ArrayList;
     40 import java.util.HashSet;
     41 import java.util.Objects;
     42 
     43 
     44 /**
     45  * @hide
     46  */
     47 class IPv6TetheringInterfaceServices {
     48     private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
     49     private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
     50     private static final int RFC7421_IP_PREFIX_LENGTH = 64;
     51 
     52     private final String mIfName;
     53     private final INetworkManagementService mNMService;
     54 
     55     private NetworkInterface mNetworkInterface;
     56     private byte[] mHwAddr;
     57     private LinkProperties mLastIPv6LinkProperties;
     58     private RouterAdvertisementDaemon mRaDaemon;
     59     private RaParams mLastRaParams;
     60 
     61     IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
     62         mIfName = ifname;
     63         mNMService = nms;
     64     }
     65 
     66     public boolean start() {
     67         try {
     68             mNetworkInterface = NetworkInterface.getByName(mIfName);
     69         } catch (SocketException e) {
     70             Log.e(TAG, "Failed to find NetworkInterface for " + mIfName, e);
     71             stop();
     72             return false;
     73         }
     74 
     75         try {
     76             mHwAddr = mNetworkInterface.getHardwareAddress();
     77         } catch (SocketException e) {
     78             Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
     79             stop();
     80             return false;
     81         }
     82 
     83         final int ifindex = mNetworkInterface.getIndex();
     84         mRaDaemon = new RouterAdvertisementDaemon(mIfName, ifindex, mHwAddr);
     85         if (!mRaDaemon.start()) {
     86             stop();
     87             return false;
     88         }
     89 
     90         return true;
     91     }
     92 
     93     public void stop() {
     94         mNetworkInterface = null;
     95         mHwAddr = null;
     96         setRaParams(null);
     97 
     98         if (mRaDaemon != null) {
     99             mRaDaemon.stop();
    100             mRaDaemon = null;
    101         }
    102     }
    103 
    104     // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
    105     // LinkProperties. These have extraneous data filtered out and only the
    106     // necessary prefixes included (per its prefix distribution policy).
    107     //
    108     // TODO: Evaluate using a data structure than is more directly suited to
    109     // communicating only the relevant information.
    110     public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
    111         if (mRaDaemon == null) return;
    112 
    113         // Avoid unnecessary work on spurious updates.
    114         if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
    115             return;
    116         }
    117 
    118         RaParams params = null;
    119 
    120         if (v6only != null) {
    121             params = new RaParams();
    122             params.mtu = v6only.getMtu();
    123             params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
    124 
    125             for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
    126                 if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
    127 
    128                 final IpPrefix prefix = new IpPrefix(
    129                         linkAddr.getAddress(), linkAddr.getPrefixLength());
    130                 params.prefixes.add(prefix);
    131 
    132                 final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
    133                 if (dnsServer != null) {
    134                     params.dnses.add(dnsServer);
    135                 }
    136             }
    137         }
    138         // If v6only is null, we pass in null to setRaParams(), which handles
    139         // deprecation of any existing RA data.
    140 
    141         setRaParams(params);
    142         mLastIPv6LinkProperties = v6only;
    143     }
    144 
    145 
    146     private void configureLocalRoutes(
    147             HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
    148         // [1] Remove the routes that are deprecated.
    149         if (!deprecatedPrefixes.isEmpty()) {
    150             final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
    151             try {
    152                 final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
    153                 if (removalFailures > 0) {
    154                     Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
    155                             removalFailures));
    156                 }
    157             } catch (RemoteException e) {
    158                 Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
    159             }
    160         }
    161 
    162         // [2] Add only the routes that have not previously been added.
    163         if (newPrefixes != null && !newPrefixes.isEmpty()) {
    164             HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
    165             if (mLastRaParams != null) {
    166                 addedPrefixes.removeAll(mLastRaParams.prefixes);
    167             }
    168 
    169             if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
    170                 // We need to be able to send unicast RAs, and clients might
    171                 // like to ping the default router's link-local address.  Note
    172                 // that we never remove the link-local route from the network
    173                 // until Tethering disables tethering on the interface. We
    174                 // only need to add the link-local prefix once, but in the
    175                 // event we add it more than once netd silently ignores EEXIST.
    176                 addedPrefixes.add(LINK_LOCAL_PREFIX);
    177             }
    178 
    179             if (!addedPrefixes.isEmpty()) {
    180                 final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
    181                 try {
    182                     // It's safe to call addInterfaceToLocalNetwork() even if
    183                     // the interface is already in the local_network. Note also
    184                     // that adding routes that already exist does not cause an
    185                     // error (EEXIST is silently ignored).
    186                     mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
    187                 } catch (RemoteException e) {
    188                     Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
    189                 }
    190             }
    191         }
    192     }
    193 
    194     private void configureLocalDns(
    195             HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
    196         INetd netd = getNetdServiceOrNull();
    197         if (netd == null) {
    198             if (newDnses != null) newDnses.clear();
    199             Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
    200             return;
    201         }
    202 
    203         // [1] Remove deprecated local DNS IP addresses.
    204         if (!deprecatedDnses.isEmpty()) {
    205             for (Inet6Address dns : deprecatedDnses) {
    206                 final String dnsString = dns.getHostAddress();
    207                 try {
    208                     netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
    209                 } catch (ServiceSpecificException | RemoteException e) {
    210                     Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
    211                 }
    212             }
    213         }
    214 
    215         // [2] Add only the local DNS IP addresses that have not previously been added.
    216         if (newDnses != null && !newDnses.isEmpty()) {
    217             final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
    218             if (mLastRaParams != null) {
    219                 addedDnses.removeAll(mLastRaParams.dnses);
    220             }
    221 
    222             for (Inet6Address dns : addedDnses) {
    223                 final String dnsString = dns.getHostAddress();
    224                 try {
    225                     netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
    226                 } catch (ServiceSpecificException | RemoteException e) {
    227                     Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
    228                     newDnses.remove(dns);
    229                 }
    230             }
    231         }
    232 
    233         try {
    234             netd.tetherApplyDnsInterfaces();
    235         } catch (ServiceSpecificException | RemoteException e) {
    236             Log.e(TAG, "Failed to update local DNS caching server");
    237             if (newDnses != null) newDnses.clear();
    238         }
    239     }
    240 
    241     private void setRaParams(RaParams newParams) {
    242         if (mRaDaemon != null) {
    243             final RaParams deprecatedParams =
    244                     RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
    245 
    246             configureLocalRoutes(deprecatedParams.prefixes,
    247                     (newParams != null) ? newParams.prefixes : null);
    248 
    249             configureLocalDns(deprecatedParams.dnses,
    250                     (newParams != null) ? newParams.dnses : null);
    251 
    252             mRaDaemon.buildNewRa(deprecatedParams, newParams);
    253         }
    254 
    255         mLastRaParams = newParams;
    256     }
    257 
    258     // Accumulate routes representing "prefixes to be assigned to the local
    259     // interface", for subsequent modification of local_network routing.
    260     private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
    261         final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
    262         for (IpPrefix ipp : prefixes) {
    263             localRoutes.add(new RouteInfo(ipp, null, mIfName));
    264         }
    265         return localRoutes;
    266     }
    267 
    268     private INetd getNetdServiceOrNull() {
    269         if (mNMService != null) {
    270             try {
    271                 return mNMService.getNetdService();
    272             } catch (RemoteException ignored) {
    273                 // This blocks until netd can be reached, but it can return
    274                 // null during a netd crash.
    275             }
    276         }
    277         return null;
    278     }
    279 
    280     // Given a prefix like 2001:db8::/64 return 2001:db8::1.
    281     private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
    282         final byte[] dnsBytes = localPrefix.getRawAddress();
    283         dnsBytes[dnsBytes.length - 1] = 0x1;
    284         try {
    285             return Inet6Address.getByAddress(null, dnsBytes, 0);
    286         } catch (UnknownHostException e) {
    287             Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
    288             return null;
    289         }
    290     }
    291 }
    292