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