• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.wifi.util;
18 
19 import android.content.Context;
20 import android.net.INetd;
21 import android.net.INetdUnsolicitedEventListener;
22 import android.net.InetAddresses;
23 import android.net.InterfaceConfiguration;
24 import android.net.InterfaceConfigurationParcel;
25 import android.net.IpPrefix;
26 import android.net.LinkAddress;
27 import android.net.RouteInfo;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.RemoteException;
31 import android.os.ServiceSpecificException;
32 import android.text.TextUtils;
33 import android.util.Log;
34 
35 import java.net.InetAddress;
36 import java.util.ArrayList;
37 import java.util.HashSet;
38 import java.util.List;
39 import java.util.Set;
40 
41 /**
42  * This is a simple wrapper over INetd calls used by wifi stack.
43  *
44  * Based on {@link com.android.server.NetworkManagementService}
45  */
46 public class NetdWrapper {
47     private static final String TAG = "NetdWrapper";
48     static final boolean MODIFY_OPERATION_ADD = true;
49     static final boolean MODIFY_OPERATION_REMOVE = false;
50 
51     private final INetd mNetdService;
52     private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
53     private final Handler mHandler;
54     private final Set<NetdEventObserver> mObservers = new HashSet<>();
55 
56     /**
57      * Observer for iface events.
58      */
59     public interface NetdEventObserver {
60         /**
61          * Interface configuration status has changed.
62          *
63          * @param iface The interface.
64          * @param up True if the interface has been enabled.
65          */
interfaceStatusChanged(String iface, boolean up)66         void interfaceStatusChanged(String iface, boolean up);
67         /**
68          * Interface physical-layer link state has changed.  For Ethernet,
69          * this method is invoked when the cable is plugged in or unplugged.
70          *
71          * @param iface The interface.
72          * @param up  True if the physical link-layer connection signal is valid.
73          */
interfaceLinkStateChanged(String iface, boolean up)74         void interfaceLinkStateChanged(String iface, boolean up);
75     }
76 
77     private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {
78         @Override
onInterfaceClassActivityChanged(boolean isActive, int label, long timestamp, int uid)79         public void onInterfaceClassActivityChanged(boolean isActive,
80                 int label, long timestamp, int uid) throws RemoteException {
81             // Unused.
82         }
83 
84         @Override
onQuotaLimitReached(String alertName, String ifName)85         public void onQuotaLimitReached(String alertName, String ifName)
86                 throws RemoteException {
87             // Unused.
88         }
89 
90         @Override
onInterfaceDnsServerInfo(String ifName, long lifetime, String[] servers)91         public void onInterfaceDnsServerInfo(String ifName,
92                 long lifetime, String[] servers) throws RemoteException {
93             // Unused.
94         }
95 
96         @Override
onInterfaceAddressUpdated(String addr, String ifName, int flags, int scope)97         public void onInterfaceAddressUpdated(String addr,
98                 String ifName, int flags, int scope) throws RemoteException {
99             // Unused.
100         }
101 
102         @Override
onInterfaceAddressRemoved(String addr, String ifName, int flags, int scope)103         public void onInterfaceAddressRemoved(String addr,
104                 String ifName, int flags, int scope) throws RemoteException {
105             // Unused.
106         }
107 
108         @Override
onInterfaceAdded(String ifName)109         public void onInterfaceAdded(String ifName) throws RemoteException {
110             // Unused.
111         }
112 
113         @Override
onInterfaceRemoved(String ifName)114         public void onInterfaceRemoved(String ifName) throws RemoteException {
115             // Unused.
116         }
117 
118         @Override
onInterfaceChanged(String ifName, boolean up)119         public void onInterfaceChanged(String ifName, boolean up)
120                 throws RemoteException {
121             mHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));
122         }
123 
124         @Override
onInterfaceLinkStateChanged(String ifName, boolean up)125         public void onInterfaceLinkStateChanged(String ifName, boolean up)
126                 throws RemoteException {
127             mHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));
128         }
129 
130         @Override
onRouteChanged(boolean updated, String route, String gateway, String ifName)131         public void onRouteChanged(boolean updated,
132                 String route, String gateway, String ifName) throws RemoteException {
133             // Unused.
134         }
135 
136         @Override
onStrictCleartextDetected(int uid, String hex)137         public void onStrictCleartextDetected(int uid, String hex) throws RemoteException {
138             // Unused.
139         }
140 
141         @Override
getInterfaceVersion()142         public int getInterfaceVersion() {
143             return INetdUnsolicitedEventListener.VERSION;
144         }
145 
146         @Override
getInterfaceHash()147         public String getInterfaceHash() {
148             return INetdUnsolicitedEventListener.HASH;
149         }
150     }
151 
NetdWrapper(Context context, Handler handler)152     public NetdWrapper(Context context, Handler handler) {
153         mNetdService = INetd.Stub.asInterface(
154                 (IBinder) context.getSystemService(Context.NETD_SERVICE));
155         mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();
156         mHandler = handler;
157 
158         try {
159             mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
160         } catch (RemoteException | ServiceSpecificException e) {
161             Log.e(TAG, "Failed to set Netd unsolicited event listener " + e);
162         }
163     }
164 
modifyInterfaceInNetwork(boolean add, int netId, String iface)165     private void modifyInterfaceInNetwork(boolean add, int netId, String iface) {
166         try {
167             if (add) {
168                 mNetdService.networkAddInterface(netId, iface);
169             } else {
170                 mNetdService.networkRemoveInterface(netId, iface);
171             }
172         } catch (RemoteException | ServiceSpecificException e) {
173             throw new IllegalStateException(e);
174         }
175     }
176 
modifyRoute(boolean add, int netId, RouteInfo route)177     private void modifyRoute(boolean add, int netId, RouteInfo route) {
178         final String ifName = route.getInterface();
179         final String dst = route.getDestination().toString();
180         final String nextHop;
181 
182         switch (route.getType()) {
183             case RouteInfo.RTN_UNICAST:
184                 if (route.hasGateway()) {
185                     nextHop = route.getGateway().getHostAddress();
186                 } else {
187                     nextHop = INetd.NEXTHOP_NONE;
188                 }
189                 break;
190             case RouteInfo.RTN_UNREACHABLE:
191                 nextHop = INetd.NEXTHOP_UNREACHABLE;
192                 break;
193             case RouteInfo.RTN_THROW:
194                 nextHop = INetd.NEXTHOP_THROW;
195                 break;
196             default:
197                 nextHop = INetd.NEXTHOP_NONE;
198                 break;
199         }
200         try {
201             if (add) {
202                 mNetdService.networkAddRoute(netId, ifName, dst, nextHop);
203             } else {
204                 mNetdService.networkRemoveRoute(netId, ifName, dst, nextHop);
205             }
206         } catch (RemoteException | ServiceSpecificException e) {
207             throw new IllegalStateException(e);
208         }
209     }
210 
211     /**
212      * Add iface to local network.
213      */
addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes)214     public void addInterfaceToLocalNetwork(String iface, List<RouteInfo> routes) {
215         modifyInterfaceInNetwork(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, iface);
216 
217         for (RouteInfo route : routes) {
218             if (!route.isDefaultRoute()) {
219                 modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID, route);
220             }
221         }
222 
223         // IPv6 link local should be activated always.
224         modifyRoute(MODIFY_OPERATION_ADD, INetd.LOCAL_NET_ID,
225                 new RouteInfo(new IpPrefix("fe80::/64"), null, iface, RouteInfo.RTN_UNICAST));
226     }
227 
228     /**
229      * Remove iface from local network.
230      */
removeInterfaceFromLocalNetwork(String iface)231     public void removeInterfaceFromLocalNetwork(String iface) {
232         modifyInterfaceInNetwork(MODIFY_OPERATION_REMOVE, INetd.LOCAL_NET_ID, iface);
233     }
234 
235     /**
236      * Clear iface addresses.
237      */
clearInterfaceAddresses(String iface)238     public void clearInterfaceAddresses(String iface) {
239         try {
240             mNetdService.interfaceClearAddrs(iface);
241         } catch (RemoteException | ServiceSpecificException e) {
242             throw new IllegalStateException(e);
243         }
244     }
245 
246     /**
247      * Enable IPv6 on iface.
248      */
enableIpv6(String iface)249     public void enableIpv6(String iface) {
250         try {
251             mNetdService.interfaceSetEnableIPv6(iface, true);
252         } catch (RemoteException | ServiceSpecificException e) {
253             throw new IllegalStateException(e);
254         }
255     }
256 
257     /**
258      * Disable IPv6 on iface.
259      */
disableIpv6(String iface)260     public void disableIpv6(String iface) {
261         try {
262             mNetdService.interfaceSetEnableIPv6(iface, false);
263         } catch (RemoteException | ServiceSpecificException e) {
264             throw new IllegalStateException(e);
265         }
266     }
267 
268     /**
269      * Convert InterfaceConfiguration to InterfaceConfigurationParcel with given ifname.
270      */
toStableParcel(InterfaceConfiguration cfg, String iface)271     private static InterfaceConfigurationParcel toStableParcel(InterfaceConfiguration cfg,
272             String iface) {
273         InterfaceConfigurationParcel cfgParcel = new InterfaceConfigurationParcel();
274         cfgParcel.ifName = iface;
275         String hwAddr = cfg.getHardwareAddress();
276         if (!TextUtils.isEmpty(hwAddr)) {
277             cfgParcel.hwAddr = hwAddr;
278         } else {
279             cfgParcel.hwAddr = "";
280         }
281         cfgParcel.ipv4Addr = cfg.getLinkAddress().getAddress().getHostAddress();
282         cfgParcel.prefixLength = cfg.getLinkAddress().getPrefixLength();
283         ArrayList<String> flags = new ArrayList<>();
284         for (String flag : cfg.getFlags()) {
285             flags.add(flag);
286         }
287         cfgParcel.flags = flags.toArray(new String[0]);
288 
289         return cfgParcel;
290     }
291 
292     /**
293      * Construct InterfaceConfiguration from InterfaceConfigurationParcel.
294      */
fromStableParcel(InterfaceConfigurationParcel p)295     private static InterfaceConfiguration fromStableParcel(InterfaceConfigurationParcel p) {
296         InterfaceConfiguration cfg = new InterfaceConfiguration();
297         cfg.setHardwareAddress(p.hwAddr);
298 
299         final InetAddress addr = InetAddresses.parseNumericAddress(p.ipv4Addr);
300         cfg.setLinkAddress(new LinkAddress(addr, p.prefixLength));
301         for (String flag : p.flags) {
302             cfg.setFlag(flag);
303         }
304         return cfg;
305     }
306 
getInterfaceConfig(String iface)307     private InterfaceConfiguration getInterfaceConfig(String iface) {
308         final InterfaceConfigurationParcel result;
309         try {
310             result = mNetdService.interfaceGetCfg(iface);
311         } catch (RemoteException | ServiceSpecificException e) {
312             throw new IllegalStateException(e);
313         }
314 
315         try {
316             final InterfaceConfiguration cfg = fromStableParcel(result);
317             return cfg;
318         } catch (IllegalArgumentException iae) {
319             throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
320         }
321     }
322 
setInterfaceConfig(String iface, InterfaceConfiguration cfg)323     private void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
324         LinkAddress linkAddr = cfg.getLinkAddress();
325         if (linkAddr == null || linkAddr.getAddress() == null) {
326             throw new IllegalStateException("Null LinkAddress given");
327         }
328         final InterfaceConfigurationParcel cfgParcel = toStableParcel(cfg, iface);
329 
330         try {
331             mNetdService.interfaceSetCfg(cfgParcel);
332         } catch (RemoteException | ServiceSpecificException e) {
333             throw new IllegalStateException(e);
334         }
335     }
336 
337     /**
338      * List all tethered interfaces.
339      */
listTetheredInterfaces()340     public String[] listTetheredInterfaces() {
341         try {
342             return mNetdService.tetherInterfaceList();
343         } catch (RemoteException | ServiceSpecificException e) {
344             throw new IllegalStateException(e);
345         }
346     }
347 
348     /**
349      * Register a new netd event observer.
350      */
registerObserver(NetdEventObserver observer)351     public void registerObserver(NetdEventObserver observer) {
352         mObservers.add(observer);
353     }
354 
355     /**
356      * Unregister a new netd event observer.
357      */
unregisterObserver(NetdEventObserver observer)358     public void unregisterObserver(NetdEventObserver observer) {
359         mObservers.remove(observer);
360     }
361 
362     /**
363      * Set iface down.
364      */
setInterfaceDown(String iface)365     public void setInterfaceDown(String iface) {
366         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
367         ifcg.setInterfaceDown();
368         setInterfaceConfig(iface, ifcg);
369     }
370 
371     /**
372      * Set iface up.
373      */
setInterfaceUp(String iface)374     public void setInterfaceUp(String iface) {
375         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
376         ifcg.setInterfaceUp();
377         setInterfaceConfig(iface, ifcg);
378     }
379 
380     /**
381      * Returns whether iface is up.
382      */
isInterfaceUp(String iface)383     public boolean isInterfaceUp(String iface) {
384         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
385         return ifcg.isUp();
386     }
387 
388     /**
389      * Set iface link address.
390      */
setInterfaceLinkAddress(String iface, LinkAddress linkAddress)391     public void setInterfaceLinkAddress(String iface, LinkAddress linkAddress) {
392         final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
393         ifcg.setLinkAddress(linkAddress);
394         ifcg.setInterfaceUp();
395         setInterfaceConfig(iface, ifcg);
396     }
397 
398     /**
399      * Set iface IPv6 privacy extensions.
400      */
setInterfaceIpv6PrivacyExtensions(String iface, boolean enable)401     public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) {
402         try {
403             mNetdService.interfaceSetIPv6PrivacyExtensions(iface, enable);
404         } catch (RemoteException | ServiceSpecificException e) {
405             throw new IllegalStateException(e);
406         }
407     }
408 
409     /**
410      * Start tethering.
411      */
startTethering(String[] dhcpRange)412     public void startTethering(String[] dhcpRange) {
413         // an odd number of addrs will fail
414         try {
415             mNetdService.tetherStart(dhcpRange);
416         } catch (RemoteException | ServiceSpecificException e) {
417             throw new IllegalStateException(e);
418         }
419     }
420 
421     /**
422      * Stop tethering.
423      */
stopTethering()424     public void stopTethering() {
425         try {
426             mNetdService.tetherStop();
427         } catch (RemoteException | ServiceSpecificException e) {
428             throw new IllegalStateException(e);
429         }
430     }
431 
432     /**
433      * Add tethering on iface.
434      */
tetherInterface(String iface)435     public void tetherInterface(String iface) {
436         try {
437             mNetdService.tetherInterfaceAdd(iface);
438         } catch (RemoteException | ServiceSpecificException e) {
439             throw new IllegalStateException(e);
440         }
441         List<RouteInfo> routes = new ArrayList<>();
442         // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it
443         // suitable to use as a route destination.
444         LinkAddress dest = getInterfaceConfig(iface).getLinkAddress();
445         RouteInfo route = new RouteInfo(
446                 new IpPrefix(dest.getAddress(), dest.getPrefixLength()),
447                 null, null, RouteInfo.RTN_UNICAST);
448         routes.add(route);
449         addInterfaceToLocalNetwork(iface, routes);
450     }
451 
452     /**
453      * Remove tethering on iface.
454      */
untetherInterface(String iface)455     public void untetherInterface(String iface) {
456         try {
457             mNetdService.tetherInterfaceRemove(iface);
458         } catch (RemoteException | ServiceSpecificException e) {
459             throw new IllegalStateException(e);
460         } finally {
461             removeInterfaceFromLocalNetwork(iface);
462         }
463     }
464 
465     /**
466      * Returns whether tethering has been started.
467      */
isTetheringStarted()468     public boolean isTetheringStarted() {
469         try {
470             final boolean isEnabled = mNetdService.tetherIsEnabled();
471             return isEnabled;
472         } catch (RemoteException | ServiceSpecificException e) {
473             throw new IllegalStateException(e);
474         }
475     }
476 
477 
478     /**
479      * Notify our observers of an interface status change
480      */
notifyInterfaceStatusChanged(String iface, boolean up)481     private void notifyInterfaceStatusChanged(String iface, boolean up) {
482         for (NetdEventObserver observer : mObservers) {
483             observer.interfaceStatusChanged(iface, up);
484         }
485     }
486 
487     /**
488      * Notify our observers of an interface link state change
489      * (typically, an Ethernet cable has been plugged-in or unplugged).
490      */
notifyInterfaceLinkStateChanged(String iface, boolean up)491     private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
492         for (NetdEventObserver observer : mObservers) {
493             observer.interfaceLinkStateChanged(iface, up);
494         }
495     }
496 }
497 
498