1 /* 2 * Copyright (C) 2017 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.networkstack.tethering; 18 19 import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP; 20 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST; 21 import static android.net.util.TetheringUtils.uint16; 22 23 import android.annotation.NonNull; 24 import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; 25 import android.hardware.tetheroffload.control.V1_0.IOffloadControl; 26 import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback; 27 import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; 28 import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; 29 import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; 30 import android.net.netlink.NetlinkSocket; 31 import android.net.netlink.StructNlMsgHdr; 32 import android.net.util.SharedLog; 33 import android.net.util.SocketUtils; 34 import android.os.Handler; 35 import android.os.NativeHandle; 36 import android.os.RemoteException; 37 import android.system.ErrnoException; 38 import android.system.Os; 39 import android.system.OsConstants; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 43 import java.io.FileDescriptor; 44 import java.io.InterruptedIOException; 45 import java.io.IOException; 46 import java.net.SocketAddress; 47 import java.net.SocketException; 48 import java.nio.ByteBuffer; 49 import java.util.ArrayList; 50 import java.util.NoSuchElementException; 51 52 53 /** 54 * Capture tethering dependencies, for injection. 55 * 56 * @hide 57 */ 58 public class OffloadHardwareInterface { 59 private static final String TAG = OffloadHardwareInterface.class.getSimpleName(); 60 private static final String YIELDS = " -> "; 61 // Change this value to control whether tether offload is enabled or 62 // disabled by default in the absence of an explicit Settings value. 63 // See accompanying unittest to distinguish 0 from non-0 values. 64 private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0; 65 private static final String NO_INTERFACE_NAME = ""; 66 private static final String NO_IPV4_ADDRESS = ""; 67 private static final String NO_IPV4_GATEWAY = ""; 68 // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h 69 private static final int NF_NETLINK_CONNTRACK_NEW = 1; 70 private static final int NF_NETLINK_CONNTRACK_UPDATE = 2; 71 private static final int NF_NETLINK_CONNTRACK_DESTROY = 4; 72 // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h 73 public static final short NFNL_SUBSYS_CTNETLINK = 1; 74 public static final short IPCTNL_MSG_CT_GET = 1; 75 76 private final long NETLINK_MESSAGE_TIMEOUT_MS = 500; 77 78 private final Handler mHandler; 79 private final SharedLog mLog; 80 private final Dependencies mDeps; 81 private IOffloadControl mOffloadControl; 82 private TetheringOffloadCallback mTetheringOffloadCallback; 83 private ControlCallback mControlCallback; 84 85 /** The callback to notify status of offload management process. */ 86 public static class ControlCallback { 87 /** Offload started. */ onStarted()88 public void onStarted() {} 89 /** 90 * Offload stopped because an error has occurred in lower layer. 91 */ onStoppedError()92 public void onStoppedError() {} 93 /** 94 * Offload stopped because the device has moved to a bearer on which hardware offload is 95 * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will 96 * likely fail and cannot be presumed to be saved inside of the hardware management process. 97 * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin 98 * offload again. 99 */ onStoppedUnsupported()100 public void onStoppedUnsupported() {} 101 /** Indicate that offload is able to proivde support for this time. */ onSupportAvailable()102 public void onSupportAvailable() {} 103 /** Offload stopped because of usage limit reached. */ onStoppedLimitReached()104 public void onStoppedLimitReached() {} 105 106 /** Indicate to update NAT timeout. */ onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)107 public void onNatTimeoutUpdate(int proto, 108 String srcAddr, int srcPort, 109 String dstAddr, int dstPort) {} 110 } 111 112 /** The object which records Tx/Rx forwarded bytes. */ 113 public static class ForwardedStats { 114 public long rxBytes; 115 public long txBytes; 116 ForwardedStats()117 public ForwardedStats() { 118 rxBytes = 0; 119 txBytes = 0; 120 } 121 122 @VisibleForTesting ForwardedStats(long rxBytes, long txBytes)123 public ForwardedStats(long rxBytes, long txBytes) { 124 this.rxBytes = rxBytes; 125 this.txBytes = txBytes; 126 } 127 128 /** Add Tx/Rx bytes. */ add(ForwardedStats other)129 public void add(ForwardedStats other) { 130 rxBytes += other.rxBytes; 131 txBytes += other.txBytes; 132 } 133 134 /** Returns the string representation of this object. */ toString()135 public String toString() { 136 return String.format("rx:%s tx:%s", rxBytes, txBytes); 137 } 138 } 139 OffloadHardwareInterface(Handler h, SharedLog log)140 public OffloadHardwareInterface(Handler h, SharedLog log) { 141 this(h, log, new Dependencies(log)); 142 } 143 OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps)144 OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { 145 mHandler = h; 146 mLog = log.forSubComponent(TAG); 147 mDeps = deps; 148 } 149 150 /** Capture OffloadHardwareInterface dependencies, for injection. */ 151 static class Dependencies { 152 private final SharedLog mLog; 153 Dependencies(SharedLog log)154 Dependencies(SharedLog log) { 155 mLog = log; 156 } 157 getOffloadConfig()158 public IOffloadConfig getOffloadConfig() { 159 try { 160 return IOffloadConfig.getService(true /*retry*/); 161 } catch (RemoteException | NoSuchElementException e) { 162 mLog.e("getIOffloadConfig error " + e); 163 return null; 164 } 165 } 166 getOffloadControl()167 public IOffloadControl getOffloadControl() { 168 try { 169 return IOffloadControl.getService(true /*retry*/); 170 } catch (RemoteException | NoSuchElementException e) { 171 mLog.e("tethering offload control not supported: " + e); 172 return null; 173 } 174 } 175 createConntrackSocket(final int groups)176 public NativeHandle createConntrackSocket(final int groups) { 177 final FileDescriptor fd; 178 try { 179 fd = NetlinkSocket.forProto(OsConstants.NETLINK_NETFILTER); 180 } catch (ErrnoException e) { 181 mLog.e("Unable to create conntrack socket " + e); 182 return null; 183 } 184 185 final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); 186 try { 187 Os.bind(fd, sockAddr); 188 } catch (ErrnoException | SocketException e) { 189 mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); 190 try { 191 SocketUtils.closeSocket(fd); 192 } catch (IOException ie) { 193 // Nothing we can do here 194 } 195 return null; 196 } 197 try { 198 Os.connect(fd, sockAddr); 199 } catch (ErrnoException | SocketException e) { 200 mLog.e("connect to kernel fail for groups " + groups + " error: " + e); 201 try { 202 SocketUtils.closeSocket(fd); 203 } catch (IOException ie) { 204 // Nothing we can do here 205 } 206 return null; 207 } 208 209 return new NativeHandle(fd, true); 210 } 211 } 212 213 /** Get default value indicating whether offload is supported. */ getDefaultTetherOffloadDisabled()214 public int getDefaultTetherOffloadDisabled() { 215 return DEFAULT_TETHER_OFFLOAD_DISABLED; 216 } 217 218 /** 219 * Offload management process need to know conntrack rules to support NAT, but it may not have 220 * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and 221 * share them with offload management process. 222 */ initOffloadConfig()223 public boolean initOffloadConfig() { 224 final IOffloadConfig offloadConfig = mDeps.getOffloadConfig(); 225 if (offloadConfig == null) { 226 mLog.e("Could not find IOffloadConfig service"); 227 return false; 228 } 229 // Per the IConfigOffload definition: 230 // 231 // h1 provides a file descriptor bound to the following netlink groups 232 // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). 233 // 234 // h2 provides a file descriptor bound to the following netlink groups 235 // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). 236 final NativeHandle h1 = mDeps.createConntrackSocket( 237 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); 238 if (h1 == null) return false; 239 240 sendNetlinkMessage(h1, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), 241 (short) (NLM_F_REQUEST | NLM_F_DUMP)); 242 243 final NativeHandle h2 = mDeps.createConntrackSocket( 244 NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); 245 if (h2 == null) { 246 closeFdInNativeHandle(h1); 247 return false; 248 } 249 250 final CbResults results = new CbResults(); 251 try { 252 offloadConfig.setHandles(h1, h2, 253 (boolean success, String errMsg) -> { 254 results.mSuccess = success; 255 results.mErrMsg = errMsg; 256 }); 257 } catch (RemoteException e) { 258 record("initOffloadConfig, setHandles fail", e); 259 return false; 260 } 261 // Explicitly close FDs. 262 closeFdInNativeHandle(h1); 263 closeFdInNativeHandle(h2); 264 265 record("initOffloadConfig, setHandles results:", results); 266 return results.mSuccess; 267 } 268 269 @VisibleForTesting sendNetlinkMessage(@onNull NativeHandle handle, short type, short flags)270 public void sendNetlinkMessage(@NonNull NativeHandle handle, short type, short flags) { 271 final int length = StructNlMsgHdr.STRUCT_SIZE; 272 final byte[] msg = new byte[length]; 273 final StructNlMsgHdr nlh = new StructNlMsgHdr(); 274 final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); 275 nlh.nlmsg_len = length; 276 nlh.nlmsg_type = type; 277 nlh.nlmsg_flags = flags; 278 nlh.nlmsg_seq = 1; 279 nlh.pack(byteBuffer); 280 try { 281 NetlinkSocket.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length, 282 NETLINK_MESSAGE_TIMEOUT_MS); 283 } catch (ErrnoException | InterruptedIOException e) { 284 mLog.e("Unable to send netfilter message, error: " + e); 285 } 286 } 287 closeFdInNativeHandle(final NativeHandle h)288 private void closeFdInNativeHandle(final NativeHandle h) { 289 try { 290 h.close(); 291 } catch (IOException | IllegalStateException e) { 292 // IllegalStateException means fd is already closed, do nothing here. 293 // Also nothing we can do if IOException. 294 } 295 } 296 297 /** Initialize the tethering offload HAL. */ initOffloadControl(ControlCallback controlCb)298 public boolean initOffloadControl(ControlCallback controlCb) { 299 mControlCallback = controlCb; 300 301 if (mOffloadControl == null) { 302 mOffloadControl = mDeps.getOffloadControl(); 303 if (mOffloadControl == null) { 304 mLog.e("tethering IOffloadControl.getService() returned null"); 305 return false; 306 } 307 } 308 309 final String logmsg = String.format("initOffloadControl(%s)", 310 (controlCb == null) ? "null" 311 : "0x" + Integer.toHexString(System.identityHashCode(controlCb))); 312 313 mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog); 314 final CbResults results = new CbResults(); 315 try { 316 mOffloadControl.initOffload( 317 mTetheringOffloadCallback, 318 (boolean success, String errMsg) -> { 319 results.mSuccess = success; 320 results.mErrMsg = errMsg; 321 }); 322 } catch (RemoteException e) { 323 record(logmsg, e); 324 return false; 325 } 326 327 record(logmsg, results); 328 return results.mSuccess; 329 } 330 331 /** Stop IOffloadControl. */ stopOffloadControl()332 public void stopOffloadControl() { 333 if (mOffloadControl != null) { 334 try { 335 mOffloadControl.stopOffload( 336 (boolean success, String errMsg) -> { 337 if (!success) mLog.e("stopOffload failed: " + errMsg); 338 }); 339 } catch (RemoteException e) { 340 mLog.e("failed to stopOffload: " + e); 341 } 342 } 343 mOffloadControl = null; 344 mTetheringOffloadCallback = null; 345 mControlCallback = null; 346 mLog.log("stopOffloadControl()"); 347 } 348 349 /** Get Tx/Rx usage from last query. */ getForwardedStats(String upstream)350 public ForwardedStats getForwardedStats(String upstream) { 351 final String logmsg = String.format("getForwardedStats(%s)", upstream); 352 353 final ForwardedStats stats = new ForwardedStats(); 354 try { 355 mOffloadControl.getForwardedStats( 356 upstream, 357 (long rxBytes, long txBytes) -> { 358 stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; 359 stats.txBytes = (txBytes > 0) ? txBytes : 0; 360 }); 361 } catch (RemoteException e) { 362 record(logmsg, e); 363 return stats; 364 } 365 366 return stats; 367 } 368 369 /** Set local prefixes to offload management process. */ setLocalPrefixes(ArrayList<String> localPrefixes)370 public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { 371 final String logmsg = String.format("setLocalPrefixes([%s])", 372 String.join(",", localPrefixes)); 373 374 final CbResults results = new CbResults(); 375 try { 376 mOffloadControl.setLocalPrefixes(localPrefixes, 377 (boolean success, String errMsg) -> { 378 results.mSuccess = success; 379 results.mErrMsg = errMsg; 380 }); 381 } catch (RemoteException e) { 382 record(logmsg, e); 383 return false; 384 } 385 386 record(logmsg, results); 387 return results.mSuccess; 388 } 389 390 /** Set data limit value to offload management process. */ setDataLimit(String iface, long limit)391 public boolean setDataLimit(String iface, long limit) { 392 393 final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); 394 395 final CbResults results = new CbResults(); 396 try { 397 mOffloadControl.setDataLimit( 398 iface, limit, 399 (boolean success, String errMsg) -> { 400 results.mSuccess = success; 401 results.mErrMsg = errMsg; 402 }); 403 } catch (RemoteException e) { 404 record(logmsg, e); 405 return false; 406 } 407 408 record(logmsg, results); 409 return results.mSuccess; 410 } 411 412 /** Set upstream parameters to offload management process. */ setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws)413 public boolean setUpstreamParameters( 414 String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { 415 iface = (iface != null) ? iface : NO_INTERFACE_NAME; 416 v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS; 417 v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY; 418 v6gws = (v6gws != null) ? v6gws : new ArrayList<>(); 419 420 final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", 421 iface, v4addr, v4gateway, String.join(",", v6gws)); 422 423 final CbResults results = new CbResults(); 424 try { 425 mOffloadControl.setUpstreamParameters( 426 iface, v4addr, v4gateway, v6gws, 427 (boolean success, String errMsg) -> { 428 results.mSuccess = success; 429 results.mErrMsg = errMsg; 430 }); 431 } catch (RemoteException e) { 432 record(logmsg, e); 433 return false; 434 } 435 436 record(logmsg, results); 437 return results.mSuccess; 438 } 439 440 /** Add downstream prefix to offload management process. */ addDownstreamPrefix(String ifname, String prefix)441 public boolean addDownstreamPrefix(String ifname, String prefix) { 442 final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix); 443 444 final CbResults results = new CbResults(); 445 try { 446 mOffloadControl.addDownstream(ifname, prefix, 447 (boolean success, String errMsg) -> { 448 results.mSuccess = success; 449 results.mErrMsg = errMsg; 450 }); 451 } catch (RemoteException e) { 452 record(logmsg, e); 453 return false; 454 } 455 456 record(logmsg, results); 457 return results.mSuccess; 458 } 459 460 /** Remove downstream prefix from offload management process. */ removeDownstreamPrefix(String ifname, String prefix)461 public boolean removeDownstreamPrefix(String ifname, String prefix) { 462 final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix); 463 464 final CbResults results = new CbResults(); 465 try { 466 mOffloadControl.removeDownstream(ifname, prefix, 467 (boolean success, String errMsg) -> { 468 results.mSuccess = success; 469 results.mErrMsg = errMsg; 470 }); 471 } catch (RemoteException e) { 472 record(logmsg, e); 473 return false; 474 } 475 476 record(logmsg, results); 477 return results.mSuccess; 478 } 479 record(String msg, Throwable t)480 private void record(String msg, Throwable t) { 481 mLog.e(msg + YIELDS + "exception: " + t); 482 } 483 record(String msg, CbResults results)484 private void record(String msg, CbResults results) { 485 final String logmsg = msg + YIELDS + results; 486 if (!results.mSuccess) { 487 mLog.e(logmsg); 488 } else { 489 mLog.log(logmsg); 490 } 491 } 492 493 private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { 494 public final Handler handler; 495 public final ControlCallback controlCb; 496 public final SharedLog log; 497 TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog)498 TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) { 499 handler = h; 500 controlCb = cb; 501 log = sharedLog; 502 } 503 504 @Override onEvent(int event)505 public void onEvent(int event) { 506 handler.post(() -> { 507 switch (event) { 508 case OffloadCallbackEvent.OFFLOAD_STARTED: 509 controlCb.onStarted(); 510 break; 511 case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: 512 controlCb.onStoppedError(); 513 break; 514 case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: 515 controlCb.onStoppedUnsupported(); 516 break; 517 case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: 518 controlCb.onSupportAvailable(); 519 break; 520 case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: 521 controlCb.onStoppedLimitReached(); 522 break; 523 default: 524 log.e("Unsupported OffloadCallbackEvent: " + event); 525 } 526 }); 527 } 528 529 @Override updateTimeout(NatTimeoutUpdate params)530 public void updateTimeout(NatTimeoutUpdate params) { 531 handler.post(() -> { 532 controlCb.onNatTimeoutUpdate( 533 networkProtocolToOsConstant(params.proto), 534 params.src.addr, uint16(params.src.port), 535 params.dst.addr, uint16(params.dst.port)); 536 }); 537 } 538 } 539 networkProtocolToOsConstant(int proto)540 private static int networkProtocolToOsConstant(int proto) { 541 switch (proto) { 542 case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; 543 case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; 544 default: 545 // The caller checks this value and will log an error. Just make 546 // sure it won't collide with valid OsContants.IPPROTO_* values. 547 return -Math.abs(proto); 548 } 549 } 550 551 private static class CbResults { 552 boolean mSuccess; 553 String mErrMsg; 554 555 @Override toString()556 public String toString() { 557 if (mSuccess) { 558 return "ok"; 559 } else { 560 return "fail: " + mErrMsg; 561 } 562 } 563 } 564 } 565