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 com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP; 20 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.net.util.SocketUtils; 25 import android.os.Handler; 26 import android.os.NativeHandle; 27 import android.system.ErrnoException; 28 import android.system.Os; 29 import android.system.OsConstants; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.net.module.util.SharedLog; 33 import com.android.net.module.util.netlink.NetlinkUtils; 34 import com.android.net.module.util.netlink.StructNfGenMsg; 35 import com.android.net.module.util.netlink.StructNlMsgHdr; 36 37 import java.io.FileDescriptor; 38 import java.io.IOException; 39 import java.io.InterruptedIOException; 40 import java.lang.annotation.Retention; 41 import java.lang.annotation.RetentionPolicy; 42 import java.net.SocketAddress; 43 import java.net.SocketException; 44 import java.nio.ByteBuffer; 45 import java.nio.ByteOrder; 46 import java.util.ArrayList; 47 48 /** 49 * Capture tethering dependencies, for injection. 50 * 51 * @hide 52 */ 53 public class OffloadHardwareInterface { 54 private static final String TAG = OffloadHardwareInterface.class.getSimpleName(); 55 private static final String YIELDS = " -> "; 56 // Change this value to control whether tether offload is enabled or 57 // disabled by default in the absence of an explicit Settings value. 58 // See accompanying unittest to distinguish 0 from non-0 values. 59 private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0; 60 private static final String NO_INTERFACE_NAME = ""; 61 private static final String NO_IPV4_ADDRESS = ""; 62 private static final String NO_IPV4_GATEWAY = ""; 63 // Reference kernel/uapi/linux/netfilter/nfnetlink_compat.h 64 public static final int NF_NETLINK_CONNTRACK_NEW = 1; 65 public static final int NF_NETLINK_CONNTRACK_UPDATE = 2; 66 public static final int NF_NETLINK_CONNTRACK_DESTROY = 4; 67 // Reference libnetfilter_conntrack/linux_nfnetlink_conntrack.h 68 public static final short NFNL_SUBSYS_CTNETLINK = 1; 69 public static final short IPCTNL_MSG_CT_NEW = 0; 70 public static final short IPCTNL_MSG_CT_GET = 1; 71 72 private final long NETLINK_MESSAGE_TIMEOUT_MS = 500; 73 74 private final Handler mHandler; 75 private final SharedLog mLog; 76 private final Dependencies mDeps; 77 private IOffloadHal mIOffload; 78 79 // TODO: Use major-minor version control to prevent from defining new constants. 80 static final int OFFLOAD_HAL_VERSION_NONE = 0; 81 static final int OFFLOAD_HAL_VERSION_HIDL_1_0 = 1; 82 static final int OFFLOAD_HAL_VERSION_HIDL_1_1 = 2; 83 static final int OFFLOAD_HAL_VERSION_AIDL = 3; 84 /** @hide */ 85 @Retention(RetentionPolicy.SOURCE) 86 @IntDef(prefix = "OFFLOAD_HAL_VERSION_", value = { 87 OFFLOAD_HAL_VERSION_NONE, 88 OFFLOAD_HAL_VERSION_HIDL_1_0, 89 OFFLOAD_HAL_VERSION_HIDL_1_1, 90 OFFLOAD_HAL_VERSION_AIDL, 91 }) 92 public @interface OffloadHalVersion {} 93 94 @NonNull halVerToString(int version)95 static String halVerToString(int version) { 96 switch(version) { 97 case OFFLOAD_HAL_VERSION_HIDL_1_0: 98 return "HIDL 1.0"; 99 case OFFLOAD_HAL_VERSION_HIDL_1_1: 100 return "HIDL 1.1"; 101 case OFFLOAD_HAL_VERSION_AIDL: 102 return "AIDL"; 103 case OFFLOAD_HAL_VERSION_NONE: 104 return "None"; 105 default: 106 throw new IllegalArgumentException("Unsupported version int " + version); 107 } 108 } 109 110 private OffloadHalCallback mOffloadHalCallback; 111 112 /** The callback to notify status of offload management process. */ 113 public static class OffloadHalCallback { 114 /** Offload started. */ onStarted()115 public void onStarted() {} 116 /** 117 * Offload stopped because an error has occurred in lower layer. 118 */ onStoppedError()119 public void onStoppedError() {} 120 /** 121 * Offload stopped because the device has moved to a bearer on which hardware offload is 122 * not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will 123 * likely fail and cannot be presumed to be saved inside of the hardware management process. 124 * Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin 125 * offload again. 126 */ onStoppedUnsupported()127 public void onStoppedUnsupported() {} 128 /** Indicate that offload is able to proivde support for this time. */ onSupportAvailable()129 public void onSupportAvailable() {} 130 /** Offload stopped because of usage limit reached. */ onStoppedLimitReached()131 public void onStoppedLimitReached() {} 132 /** Indicate that data warning quota is reached. */ onWarningReached()133 public void onWarningReached() {} 134 135 /** Indicate to update NAT timeout. */ onNatTimeoutUpdate(int proto, String srcAddr, int srcPort, String dstAddr, int dstPort)136 public void onNatTimeoutUpdate(int proto, 137 String srcAddr, int srcPort, 138 String dstAddr, int dstPort) {} 139 } 140 141 /** The object which records Tx/Rx forwarded bytes. */ 142 public static class ForwardedStats { 143 public long rxBytes; 144 public long txBytes; 145 ForwardedStats()146 public ForwardedStats() { 147 rxBytes = 0; 148 txBytes = 0; 149 } 150 151 @VisibleForTesting ForwardedStats(long rxBytes, long txBytes)152 public ForwardedStats(long rxBytes, long txBytes) { 153 this.rxBytes = rxBytes; 154 this.txBytes = txBytes; 155 } 156 157 /** Add Tx/Rx bytes. */ add(ForwardedStats other)158 public void add(ForwardedStats other) { 159 rxBytes += other.rxBytes; 160 txBytes += other.txBytes; 161 } 162 163 /** Returns the string representation of this object. */ toString()164 public String toString() { 165 return String.format("rx:%s tx:%s", rxBytes, txBytes); 166 } 167 } 168 OffloadHardwareInterface(Handler h, SharedLog log)169 public OffloadHardwareInterface(Handler h, SharedLog log) { 170 this(h, log, new Dependencies(h, log)); 171 } 172 OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps)173 OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { 174 mHandler = h; 175 mLog = log.forSubComponent(TAG); 176 mDeps = deps; 177 } 178 179 /** Capture OffloadHardwareInterface dependencies, for injection. */ 180 static class Dependencies { 181 private final Handler mHandler; 182 private final SharedLog mLog; 183 Dependencies(Handler handler, SharedLog log)184 Dependencies(Handler handler, SharedLog log) { 185 mHandler = handler; 186 mLog = log; 187 } 188 getOffload()189 public IOffloadHal getOffload() { 190 // Prefer AIDL implementation if its service is declared. 191 IOffloadHal hal = OffloadHalAidlImpl.getIOffloadHal(mHandler, mLog); 192 if (hal == null) { 193 hal = OffloadHalHidlImpl.getIOffloadHal(mHandler, mLog); 194 } 195 return hal; 196 } 197 createConntrackSocket(final int groups)198 public NativeHandle createConntrackSocket(final int groups) { 199 final FileDescriptor fd; 200 try { 201 fd = NetlinkUtils.netlinkSocketForProto(OsConstants.NETLINK_NETFILTER); 202 } catch (ErrnoException e) { 203 mLog.e("Unable to create conntrack socket " + e); 204 return null; 205 } 206 207 final SocketAddress sockAddr = SocketUtils.makeNetlinkSocketAddress(0, groups); 208 try { 209 Os.bind(fd, sockAddr); 210 } catch (ErrnoException | SocketException e) { 211 mLog.e("Unable to bind conntrack socket for groups " + groups + " error: " + e); 212 try { 213 SocketUtils.closeSocket(fd); 214 } catch (IOException ie) { 215 // Nothing we can do here 216 } 217 return null; 218 } 219 try { 220 Os.connect(fd, sockAddr); 221 } catch (ErrnoException | SocketException e) { 222 mLog.e("connect to kernel fail for groups " + groups + " error: " + e); 223 try { 224 SocketUtils.closeSocket(fd); 225 } catch (IOException ie) { 226 // Nothing we can do here 227 } 228 return null; 229 } 230 231 return new NativeHandle(fd, true); 232 } 233 } 234 235 /** Get default value indicating whether offload is supported. */ getDefaultTetherOffloadDisabled()236 public int getDefaultTetherOffloadDisabled() { 237 return DEFAULT_TETHER_OFFLOAD_DISABLED; 238 } 239 240 @VisibleForTesting sendIpv4NfGenMsg(@onNull NativeHandle handle, short type, short flags)241 void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) { 242 final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; 243 final byte[] msg = new byte[length]; 244 final ByteBuffer byteBuffer = ByteBuffer.wrap(msg); 245 byteBuffer.order(ByteOrder.nativeOrder()); 246 247 final StructNlMsgHdr nlh = new StructNlMsgHdr(); 248 nlh.nlmsg_len = length; 249 nlh.nlmsg_type = type; 250 nlh.nlmsg_flags = flags; 251 nlh.nlmsg_seq = 0; 252 nlh.pack(byteBuffer); 253 254 // Header needs to be added to buffer since a generic netlink request is being sent. 255 final StructNfGenMsg nfh = new StructNfGenMsg((byte) OsConstants.AF_INET); 256 nfh.pack(byteBuffer); 257 258 try { 259 NetlinkUtils.sendMessage(handle.getFileDescriptor(), msg, 0 /* offset */, length, 260 NETLINK_MESSAGE_TIMEOUT_MS); 261 } catch (ErrnoException | InterruptedIOException e) { 262 mLog.e("Unable to send netfilter message, error: " + e); 263 } 264 } 265 266 @VisibleForTesting requestSocketDump(NativeHandle handle)267 void requestSocketDump(NativeHandle handle) { 268 sendIpv4NfGenMsg(handle, (short) ((NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_GET), 269 (short) (NLM_F_REQUEST | NLM_F_DUMP)); 270 } 271 maybeCloseFdInNativeHandles(final NativeHandle... handles)272 private void maybeCloseFdInNativeHandles(final NativeHandle... handles) { 273 for (NativeHandle h : handles) { 274 if (h == null) continue; 275 try { 276 h.close(); 277 } catch (IOException | IllegalStateException e) { 278 // IllegalStateException means fd is already closed, do nothing here. 279 // Also nothing we can do if IOException. 280 } 281 } 282 } 283 initWithHandles(NativeHandle h1, NativeHandle h2)284 private int initWithHandles(NativeHandle h1, NativeHandle h2) { 285 if (h1 == null || h2 == null) { 286 mLog.e("Failed to create socket."); 287 return OFFLOAD_HAL_VERSION_NONE; 288 } 289 290 requestSocketDump(h1); 291 if (!mIOffload.initOffload(h1, h2, mOffloadHalCallback)) { 292 mIOffload.stopOffload(); 293 mLog.e("Failed to initialize offload."); 294 return OFFLOAD_HAL_VERSION_NONE; 295 } 296 297 return mIOffload.getVersion(); 298 } 299 300 /** 301 * Initialize the tethering offload HAL. 302 * 303 * @return one of {@code OFFLOAD_HAL_VERSION_*} represents the HAL version, or 304 * {@link #OFFLOAD_HAL_VERSION_NONE} if failed. 305 */ initOffload(OffloadHalCallback offloadCb)306 public int initOffload(OffloadHalCallback offloadCb) { 307 if (mIOffload == null) { 308 mIOffload = mDeps.getOffload(); 309 if (mIOffload == null) { 310 mLog.i("No tethering offload HAL service found."); 311 return OFFLOAD_HAL_VERSION_NONE; 312 } 313 mLog.i("Tethering offload version " 314 + halVerToString(mIOffload.getVersion()) + " is supported."); 315 } 316 317 // Per the IOffload definition: 318 // 319 // h1 provides a file descriptor bound to the following netlink groups 320 // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). 321 // 322 // h2 provides a file descriptor bound to the following netlink groups 323 // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). 324 final NativeHandle h1 = mDeps.createConntrackSocket( 325 NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); 326 final NativeHandle h2 = mDeps.createConntrackSocket( 327 NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); 328 329 mOffloadHalCallback = offloadCb; 330 final int version = initWithHandles(h1, h2); 331 332 // Explicitly close FDs for HIDL. AIDL will pass the original FDs to the service, 333 // they shouldn't be closed here. 334 if (version < OFFLOAD_HAL_VERSION_AIDL) { 335 maybeCloseFdInNativeHandles(h1, h2); 336 } 337 return version; 338 } 339 340 /** Stop the tethering offload HAL. */ stopOffload()341 public void stopOffload() { 342 if (mIOffload != null) { 343 if (!mIOffload.stopOffload()) { 344 mLog.e("Failed to stop offload."); 345 } 346 } 347 mIOffload = null; 348 mOffloadHalCallback = null; 349 } 350 351 /** Get Tx/Rx usage from last query. */ getForwardedStats(String upstream)352 public ForwardedStats getForwardedStats(String upstream) { 353 return mIOffload.getForwardedStats(upstream); 354 } 355 356 /** Set local prefixes to offload management process. */ setLocalPrefixes(ArrayList<String> localPrefixes)357 public boolean setLocalPrefixes(ArrayList<String> localPrefixes) { 358 return mIOffload.setLocalPrefixes(localPrefixes); 359 } 360 361 /** Set data limit value to offload management process. */ setDataLimit(String iface, long limit)362 public boolean setDataLimit(String iface, long limit) { 363 return mIOffload.setDataLimit(iface, limit); 364 } 365 366 /** Set data warning and limit value to offload management process. */ setDataWarningAndLimit(String iface, long warning, long limit)367 public boolean setDataWarningAndLimit(String iface, long warning, long limit) { 368 if (mIOffload.getVersion() < OFFLOAD_HAL_VERSION_HIDL_1_1) { 369 throw new UnsupportedOperationException( 370 "setDataWarningAndLimit is not supported below HAL V1.1"); 371 } 372 return mIOffload.setDataWarningAndLimit(iface, warning, limit); 373 } 374 375 /** Set upstream parameters to offload management process. */ setUpstreamParameters( String iface, String v4addr, String v4gateway, ArrayList<String> v6gws)376 public boolean setUpstreamParameters( 377 String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) { 378 iface = (iface != null) ? iface : NO_INTERFACE_NAME; 379 v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS; 380 v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY; 381 v6gws = (v6gws != null) ? v6gws : new ArrayList<>(); 382 return mIOffload.setUpstreamParameters(iface, v4addr, v4gateway, v6gws); 383 } 384 385 /** Add downstream prefix to offload management process. */ addDownstream(String ifname, String prefix)386 public boolean addDownstream(String ifname, String prefix) { 387 return mIOffload.addDownstream(ifname, prefix); 388 } 389 390 /** Remove downstream prefix from offload management process. */ removeDownstream(String ifname, String prefix)391 public boolean removeDownstream(String ifname, String prefix) { 392 return mIOffload.removeDownstream(ifname, prefix); 393 } 394 } 395