blob: c2c1a8c5fef7f3f1beee70ec40a7eb085c2bd8b2 [file] [log] [blame]
Erik Kline1eb8c692016-07-08 17:21:26 +09001/*
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
17package com.android.server.connectivity.tethering;
18
Erik Klinefa37b2f2016-08-02 18:27:03 +090019import android.net.INetd;
Erik Kline1eb8c692016-07-08 17:21:26 +090020import android.net.IpPrefix;
21import android.net.LinkAddress;
22import android.net.LinkProperties;
23import android.net.NetworkCapabilities;
24import android.net.NetworkState;
25import android.net.RouteInfo;
26import android.net.ip.RouterAdvertisementDaemon;
27import android.net.ip.RouterAdvertisementDaemon.RaParams;
Erik Klinec1bc0be2016-07-05 10:18:42 +090028import android.net.util.NetdService;
Erik Kline1eb8c692016-07-08 17:21:26 +090029import android.os.INetworkManagementService;
Erik Kline1f4278a2016-08-16 16:46:33 +090030import android.os.ServiceSpecificException;
Erik Kline1eb8c692016-07-08 17:21:26 +090031import android.os.RemoteException;
32import android.util.Log;
Erik Klinefa37b2f2016-08-02 18:27:03 +090033import android.util.Slog;
Erik Kline1eb8c692016-07-08 17:21:26 +090034
35import java.net.Inet6Address;
36import java.net.InetAddress;
37import java.net.NetworkInterface;
38import java.net.SocketException;
Erik Klinefa37b2f2016-08-02 18:27:03 +090039import java.net.UnknownHostException;
Erik Kline1eb8c692016-07-08 17:21:26 +090040import java.util.ArrayList;
41import java.util.HashSet;
Erik Klinefa37b2f2016-08-02 18:27:03 +090042import java.util.Objects;
Erik Kline1eb8c692016-07-08 17:21:26 +090043
44
45/**
46 * @hide
47 */
48class IPv6TetheringInterfaceServices {
49 private static final String TAG = IPv6TetheringInterfaceServices.class.getSimpleName();
Erik Klinefa37b2f2016-08-02 18:27:03 +090050 private static final IpPrefix LINK_LOCAL_PREFIX = new IpPrefix("fe80::/64");
51 private static final int RFC7421_IP_PREFIX_LENGTH = 64;
Erik Kline1eb8c692016-07-08 17:21:26 +090052
53 private final String mIfName;
54 private final INetworkManagementService mNMService;
55
56 private NetworkInterface mNetworkInterface;
57 private byte[] mHwAddr;
Erik Klinefa37b2f2016-08-02 18:27:03 +090058 private LinkProperties mLastIPv6LinkProperties;
Erik Kline1eb8c692016-07-08 17:21:26 +090059 private RouterAdvertisementDaemon mRaDaemon;
60 private RaParams mLastRaParams;
61
62 IPv6TetheringInterfaceServices(String ifname, INetworkManagementService nms) {
63 mIfName = ifname;
64 mNMService = nms;
65 }
66
67 public boolean start() {
68 try {
69 mNetworkInterface = NetworkInterface.getByName(mIfName);
70 } catch (SocketException e) {
71 Log.e(TAG, "Failed to find NetworkInterface for " + mIfName, e);
72 stop();
73 return false;
74 }
75
76 try {
77 mHwAddr = mNetworkInterface.getHardwareAddress();
78 } catch (SocketException e) {
79 Log.e(TAG, "Failed to find hardware address for " + mIfName, e);
80 stop();
81 return false;
82 }
83
84 final int ifindex = mNetworkInterface.getIndex();
85 mRaDaemon = new RouterAdvertisementDaemon(mIfName, ifindex, mHwAddr);
86 if (!mRaDaemon.start()) {
87 stop();
88 return false;
89 }
90
91 return true;
92 }
93
94 public void stop() {
95 mNetworkInterface = null;
96 mHwAddr = null;
Erik Klinefa37b2f2016-08-02 18:27:03 +090097 setRaParams(null);
Erik Kline1eb8c692016-07-08 17:21:26 +090098
99 if (mRaDaemon != null) {
100 mRaDaemon.stop();
101 mRaDaemon = null;
102 }
103 }
104
105 // IPv6TetheringCoordinator sends updates with carefully curated IPv6-only
106 // LinkProperties. These have extraneous data filtered out and only the
107 // necessary prefixes included (per its prefix distribution policy).
108 //
109 // TODO: Evaluate using a data structure than is more directly suited to
110 // communicating only the relevant information.
111 public void updateUpstreamIPv6LinkProperties(LinkProperties v6only) {
112 if (mRaDaemon == null) return;
113
Erik Klinefa37b2f2016-08-02 18:27:03 +0900114 // Avoid unnecessary work on spurious updates.
115 if (Objects.equals(mLastIPv6LinkProperties, v6only)) {
Erik Kline1eb8c692016-07-08 17:21:26 +0900116 return;
117 }
118
Erik Klinefa37b2f2016-08-02 18:27:03 +0900119 RaParams params = null;
Erik Kline1eb8c692016-07-08 17:21:26 +0900120
Erik Klinefa37b2f2016-08-02 18:27:03 +0900121 if (v6only != null) {
122 params = new RaParams();
123 params.mtu = v6only.getMtu();
124 params.hasDefaultRoute = v6only.hasIPv6DefaultRoute();
Erik Kline1eb8c692016-07-08 17:21:26 +0900125
Erik Klinefa37b2f2016-08-02 18:27:03 +0900126 for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
127 if (linkAddr.getPrefixLength() != RFC7421_IP_PREFIX_LENGTH) continue;
Erik Kline1eb8c692016-07-08 17:21:26 +0900128
Erik Klinefa37b2f2016-08-02 18:27:03 +0900129 final IpPrefix prefix = new IpPrefix(
130 linkAddr.getAddress(), linkAddr.getPrefixLength());
131 params.prefixes.add(prefix);
132
133 final Inet6Address dnsServer = getLocalDnsIpFor(prefix);
134 if (dnsServer != null) {
135 params.dnses.add(dnsServer);
136 }
137 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900138 }
Erik Klinefa37b2f2016-08-02 18:27:03 +0900139 // If v6only is null, we pass in null to setRaParams(), which handles
140 // deprecation of any existing RA data.
Erik Kline1eb8c692016-07-08 17:21:26 +0900141
Erik Klinefa37b2f2016-08-02 18:27:03 +0900142 setRaParams(params);
143 mLastIPv6LinkProperties = v6only;
144 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900145
Erik Klinefa37b2f2016-08-02 18:27:03 +0900146
147 private void configureLocalRoutes(
148 HashSet<IpPrefix> deprecatedPrefixes, HashSet<IpPrefix> newPrefixes) {
149 // [1] Remove the routes that are deprecated.
150 if (!deprecatedPrefixes.isEmpty()) {
151 final ArrayList<RouteInfo> toBeRemoved = getLocalRoutesFor(deprecatedPrefixes);
152 try {
153 final int removalFailures = mNMService.removeRoutesFromLocalNetwork(toBeRemoved);
154 if (removalFailures > 0) {
155 Log.e(TAG, String.format("Failed to remove %d IPv6 routes from local table.",
156 removalFailures));
157 }
158 } catch (RemoteException e) {
159 Log.e(TAG, "Failed to remove IPv6 routes from local table: ", e);
Erik Kline1eb8c692016-07-08 17:21:26 +0900160 }
161 }
162
Erik Klinefa37b2f2016-08-02 18:27:03 +0900163 // [2] Add only the routes that have not previously been added.
164 if (newPrefixes != null && !newPrefixes.isEmpty()) {
165 HashSet<IpPrefix> addedPrefixes = (HashSet) newPrefixes.clone();
166 if (mLastRaParams != null) {
167 addedPrefixes.removeAll(mLastRaParams.prefixes);
168 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900169
Erik Klinefa37b2f2016-08-02 18:27:03 +0900170 if (mLastRaParams == null || mLastRaParams.prefixes.isEmpty()) {
171 // We need to be able to send unicast RAs, and clients might
172 // like to ping the default router's link-local address. Note
173 // that we never remove the link-local route from the network
Erik Klined1dcbef2016-08-10 10:00:32 +0900174 // until Tethering disables tethering on the interface. We
175 // only need to add the link-local prefix once, but in the
176 // event we add it more than once netd silently ignores EEXIST.
Erik Klinefa37b2f2016-08-02 18:27:03 +0900177 addedPrefixes.add(LINK_LOCAL_PREFIX);
178 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900179
Erik Klinefa37b2f2016-08-02 18:27:03 +0900180 if (!addedPrefixes.isEmpty()) {
181 final ArrayList<RouteInfo> toBeAdded = getLocalRoutesFor(addedPrefixes);
Erik Kline1eb8c692016-07-08 17:21:26 +0900182 try {
Erik Klinefa37b2f2016-08-02 18:27:03 +0900183 // It's safe to call addInterfaceToLocalNetwork() even if
Erik Klined1dcbef2016-08-10 10:00:32 +0900184 // the interface is already in the local_network. Note also
185 // that adding routes that already exist does not cause an
186 // error (EEXIST is silently ignored).
Erik Klinefa37b2f2016-08-02 18:27:03 +0900187 mNMService.addInterfaceToLocalNetwork(mIfName, toBeAdded);
Erik Kline1eb8c692016-07-08 17:21:26 +0900188 } catch (RemoteException e) {
189 Log.e(TAG, "Failed to add IPv6 routes to local table: ", e);
190 }
191 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900192 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900193 }
194
Erik Klinefa37b2f2016-08-02 18:27:03 +0900195 private void configureLocalDns(
196 HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
Erik Klinec1bc0be2016-07-05 10:18:42 +0900197 final INetd netd = NetdService.getInstance();
Erik Klinefa37b2f2016-08-02 18:27:03 +0900198 if (netd == null) {
199 if (newDnses != null) newDnses.clear();
200 Log.e(TAG, "No netd service instance available; not setting local IPv6 addresses");
201 return;
Erik Kline1eb8c692016-07-08 17:21:26 +0900202 }
203
Erik Klinefa37b2f2016-08-02 18:27:03 +0900204 // [1] Remove deprecated local DNS IP addresses.
205 if (!deprecatedDnses.isEmpty()) {
206 for (Inet6Address dns : deprecatedDnses) {
207 final String dnsString = dns.getHostAddress();
208 try {
209 netd.interfaceDelAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
Erik Kline1f4278a2016-08-16 16:46:33 +0900210 } catch (ServiceSpecificException | RemoteException e) {
Erik Klinefa37b2f2016-08-02 18:27:03 +0900211 Log.e(TAG, "Failed to remove local dns IP: " + dnsString, e);
212 }
213 }
214 }
215
216 // [2] Add only the local DNS IP addresses that have not previously been added.
217 if (newDnses != null && !newDnses.isEmpty()) {
218 final HashSet<Inet6Address> addedDnses = (HashSet) newDnses.clone();
219 if (mLastRaParams != null) {
220 addedDnses.removeAll(mLastRaParams.dnses);
221 }
222
223 for (Inet6Address dns : addedDnses) {
224 final String dnsString = dns.getHostAddress();
225 try {
226 netd.interfaceAddAddress(mIfName, dnsString, RFC7421_IP_PREFIX_LENGTH);
Erik Kline1f4278a2016-08-16 16:46:33 +0900227 } catch (ServiceSpecificException | RemoteException e) {
Erik Klinefa37b2f2016-08-02 18:27:03 +0900228 Log.e(TAG, "Failed to add local dns IP: " + dnsString, e);
229 newDnses.remove(dns);
230 }
231 }
232 }
233
234 try {
235 netd.tetherApplyDnsInterfaces();
Erik Kline1f4278a2016-08-16 16:46:33 +0900236 } catch (ServiceSpecificException | RemoteException e) {
Erik Klinefa37b2f2016-08-02 18:27:03 +0900237 Log.e(TAG, "Failed to update local DNS caching server");
238 if (newDnses != null) newDnses.clear();
239 }
240 }
241
242 private void setRaParams(RaParams newParams) {
243 if (mRaDaemon != null) {
244 final RaParams deprecatedParams =
245 RaParams.getDeprecatedRaParams(mLastRaParams, newParams);
246
247 configureLocalRoutes(deprecatedParams.prefixes,
248 (newParams != null) ? newParams.prefixes : null);
249
250 configureLocalDns(deprecatedParams.dnses,
251 (newParams != null) ? newParams.dnses : null);
252
253 mRaDaemon.buildNewRa(deprecatedParams, newParams);
254 }
255
256 mLastRaParams = newParams;
257 }
258
259 // Accumulate routes representing "prefixes to be assigned to the local
260 // interface", for subsequent modification of local_network routing.
261 private ArrayList<RouteInfo> getLocalRoutesFor(HashSet<IpPrefix> prefixes) {
262 final ArrayList<RouteInfo> localRoutes = new ArrayList<RouteInfo>();
263 for (IpPrefix ipp : prefixes) {
264 localRoutes.add(new RouteInfo(ipp, null, mIfName));
265 }
266 return localRoutes;
267 }
268
Erik Klinefa37b2f2016-08-02 18:27:03 +0900269 // Given a prefix like 2001:db8::/64 return 2001:db8::1.
270 private static Inet6Address getLocalDnsIpFor(IpPrefix localPrefix) {
271 final byte[] dnsBytes = localPrefix.getRawAddress();
272 dnsBytes[dnsBytes.length - 1] = 0x1;
273 try {
274 return Inet6Address.getByAddress(null, dnsBytes, 0);
275 } catch (UnknownHostException e) {
276 Slog.wtf(TAG, "Failed to construct Inet6Address from: " + localPrefix);
277 return null;
278 }
Erik Kline1eb8c692016-07-08 17:21:26 +0900279 }
280}