• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms)61     IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
62         mIfName = ifname;
63         mNMService = nms;
64     }
65 
start()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 
stop()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.
updateUpstreamIPv6LinkProperties(LinkProperties v6only)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 
configureLocalRoutes( HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes)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 
configureLocalDns( HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses)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 
setRaParams(RaParams newParams)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.
getLocalRoutesFor(HashSet<IpPrefix> prefixes)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 
getNetdServiceOrNull()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.
getLocalDnsIpFor(IpPrefix localPrefix)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