• 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.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 
IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList)52     public IPv6TetheringCoordinator(ArrayList<TetherInterfaceStateMachine> notifyList) {
53         mNotifyList = notifyList;
54         mActiveDownstreams = new LinkedList<>();
55     }
56 
addActiveDownstream(TetherInterfaceStateMachine downstream)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 
removeActiveDownstream(TetherInterfaceStateMachine downstream)66     public void removeActiveDownstream(TetherInterfaceStateMachine downstream) {
67         stopIPv6TetheringOn(downstream);
68         if (mActiveDownstreams.remove(downstream)) {
69             updateIPv6TetheringInterfaces();
70         }
71     }
72 
updateUpstreamNetworkState(NetworkState ns)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 
stopIPv6TetheringOnAllInterfaces()92     private void stopIPv6TetheringOnAllInterfaces() {
93         for (TetherInterfaceStateMachine sm : mNotifyList) {
94             stopIPv6TetheringOn(sm);
95         }
96     }
97 
setUpstreamNetworkState(NetworkState ns)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 
updateIPv6TetheringInterfaces()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 
getInterfaceIPv6LinkProperties(TetherInterfaceStateMachine sm)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 
canTetherIPv6(NetworkState ns)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 
getIPv6OnlyLinkProperties(LinkProperties lp)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.
isIPv6GlobalAddress(InetAddress ip)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 
toDebugString(NetworkState ns)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 
stopIPv6TetheringOn(TetherInterfaceStateMachine sm)276     private static void stopIPv6TetheringOn(TetherInterfaceStateMachine sm) {
277         sm.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, null);
278     }
279 }
280