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