1 /* 2 * Copyright (C) 2025 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.net; 18 19 import static android.net.L2capNetworkSpecifier.HEADER_COMPRESSION_6LOWPAN; 20 21 import android.annotation.Nullable; 22 import android.bluetooth.BluetoothSocket; 23 import android.content.Context; 24 import android.net.L2capNetworkSpecifier; 25 import android.net.LinkProperties; 26 import android.net.NetworkAgent; 27 import android.net.NetworkAgentConfig; 28 import android.net.NetworkCapabilities; 29 import android.net.NetworkProvider; 30 import android.net.NetworkScore; 31 import android.net.ip.IIpClient; 32 import android.net.ip.IpClientCallbacks; 33 import android.net.ip.IpClientManager; 34 import android.net.ip.IpClientUtil; 35 import android.net.shared.ProvisioningConfiguration; 36 import android.os.ConditionVariable; 37 import android.os.Handler; 38 import android.os.ParcelFileDescriptor; 39 import android.util.Log; 40 41 import com.android.server.L2capNetworkProvider; 42 43 public class L2capNetwork { 44 private static final NetworkScore NETWORK_SCORE = new NetworkScore.Builder().build(); 45 private final String mLogTag; 46 private final Handler mHandler; 47 private final L2capPacketForwarder mForwarder; 48 private final NetworkCapabilities mNetworkCapabilities; 49 private final NetworkAgent mNetworkAgent; 50 51 /** IpClient wrapper to handle IPv6 link-local provisioning for L2CAP tun. 52 * 53 * Note that the IpClient does not need to be stopped. 54 */ 55 public static class L2capIpClient extends IpClientCallbacks { 56 private final String mLogTag; 57 private final ConditionVariable mOnIpClientCreatedCv = new ConditionVariable(false); 58 private final ConditionVariable mOnProvisioningSuccessCv = new ConditionVariable(false); 59 @Nullable 60 private IpClientManager mIpClient; 61 @Nullable 62 private volatile LinkProperties mLinkProperties; 63 L2capIpClient(String logTag, Context context, String ifname)64 public L2capIpClient(String logTag, Context context, String ifname) { 65 mLogTag = logTag; 66 IpClientUtil.makeIpClient(context, ifname, this); 67 } 68 69 @Override onIpClientCreated(IIpClient ipClient)70 public void onIpClientCreated(IIpClient ipClient) { 71 mIpClient = new IpClientManager(ipClient, mLogTag); 72 mOnIpClientCreatedCv.open(); 73 } 74 75 @Override onProvisioningSuccess(LinkProperties lp)76 public void onProvisioningSuccess(LinkProperties lp) { 77 Log.d(mLogTag, "Successfully provisioned l2cap tun: " + lp); 78 mLinkProperties = lp; 79 mOnProvisioningSuccessCv.open(); 80 } 81 82 @Override onProvisioningFailure(LinkProperties lp)83 public void onProvisioningFailure(LinkProperties lp) { 84 Log.i(mLogTag, "Failed to provision l2cap tun: " + lp); 85 mLinkProperties = null; 86 mOnProvisioningSuccessCv.open(); 87 } 88 89 /** 90 * Starts IPv6 link-local provisioning. 91 * 92 * @return LinkProperties on success, null on failure. 93 */ 94 @Nullable start()95 public LinkProperties start() { 96 mOnIpClientCreatedCv.block(); 97 // mIpClient guaranteed non-null. 98 final ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() 99 .withoutIPv4() 100 .withIpv6LinkLocalOnly() 101 .withRandomMacAddress() // addr_gen_mode EUI64 -> random on tun. 102 .build(); 103 mIpClient.startProvisioning(config); 104 // "Provisioning" is guaranteed to succeed as link-local only mode does not actually 105 // require any provisioning. 106 mOnProvisioningSuccessCv.block(); 107 return mLinkProperties; 108 } 109 } 110 111 public interface ICallback { 112 /** Called when an error is encountered */ onError(L2capNetwork network)113 void onError(L2capNetwork network); 114 /** Called when CS triggers NetworkAgent#onNetworkUnwanted */ onNetworkUnwanted(L2capNetwork network)115 void onNetworkUnwanted(L2capNetwork network); 116 } 117 L2capNetwork(String logTag, Handler handler, Context context, NetworkProvider provider, BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc, LinkProperties lp, L2capNetworkProvider.Dependencies deps, ICallback cb)118 public L2capNetwork(String logTag, Handler handler, Context context, NetworkProvider provider, 119 BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc, 120 LinkProperties lp, L2capNetworkProvider.Dependencies deps, ICallback cb) { 121 mLogTag = logTag; 122 mHandler = handler; 123 mNetworkCapabilities = nc; 124 125 final L2capNetworkSpecifier spec = (L2capNetworkSpecifier) nc.getNetworkSpecifier(); 126 final boolean compressHeaders = spec.getHeaderCompression() == HEADER_COMPRESSION_6LOWPAN; 127 128 mForwarder = deps.createL2capPacketForwarder(handler, tunFd, socket, compressHeaders, 129 () -> { 130 // TODO: add a check that this callback is invoked on the handler thread. 131 cb.onError(L2capNetwork.this); 132 }); 133 134 final NetworkAgentConfig config = new NetworkAgentConfig.Builder().build(); 135 mNetworkAgent = new NetworkAgent(context, mHandler.getLooper(), mLogTag, 136 nc, lp, NETWORK_SCORE, config, provider) { 137 @Override 138 public void onNetworkUnwanted() { 139 Log.i(mLogTag, "Network is unwanted"); 140 // TODO: add a check that this callback is invoked on the handler thread. 141 cb.onNetworkUnwanted(L2capNetwork.this); 142 } 143 }; 144 mNetworkAgent.register(); 145 mNetworkAgent.markConnected(); 146 } 147 148 /** Create an L2capNetwork or return null on failure. */ 149 @Nullable create(Handler handler, Context context, NetworkProvider provider, String ifname, BluetoothSocket socket, ParcelFileDescriptor tunFd, NetworkCapabilities nc, L2capNetworkProvider.Dependencies deps, ICallback cb)150 public static L2capNetwork create(Handler handler, Context context, NetworkProvider provider, 151 String ifname, BluetoothSocket socket, ParcelFileDescriptor tunFd, 152 NetworkCapabilities nc, L2capNetworkProvider.Dependencies deps, ICallback cb) { 153 // TODO: add a check that this function is invoked on the handler thread. 154 final String logTag = String.format("L2capNetwork[%s]", ifname); 155 156 // L2capIpClient#start() blocks until provisioning either succeeds (and returns 157 // LinkProperties) or fails (and returns null). 158 // Note that since L2capNetwork is using IPv6 link-local provisioning the most likely 159 // (only?) failure mode is due to the interface disappearing. 160 final LinkProperties lp = deps.createL2capIpClient(logTag, context, ifname).start(); 161 if (lp == null) return null; 162 163 return new L2capNetwork( 164 logTag, handler, context, provider, socket, tunFd, nc, lp, deps, cb); 165 } 166 167 /** Get the NetworkCapabilities used for this Network */ getNetworkCapabilities()168 public NetworkCapabilities getNetworkCapabilities() { 169 return mNetworkCapabilities; 170 } 171 172 /** Tear down the network and associated resources */ tearDown()173 public void tearDown() { 174 mNetworkAgent.unregister(); 175 mForwarder.tearDown(); 176 } 177 } 178