1 /* 2 * Copyright (C) 2023 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.thread; 18 19 import static android.system.OsConstants.AF_INET6; 20 import static android.system.OsConstants.EADDRINUSE; 21 import static android.system.OsConstants.IFF_MULTICAST; 22 import static android.system.OsConstants.IFF_NOARP; 23 import static android.system.OsConstants.NETLINK_ROUTE; 24 25 import static com.android.net.module.util.netlink.NetlinkConstants.RTM_NEWLINK; 26 import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IFLA_AF_SPEC; 27 import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IFLA_INET6_ADDR_GEN_MODE; 28 import static com.android.net.module.util.netlink.RtNetlinkLinkMessage.IN6_ADDR_GEN_MODE_NONE; 29 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_ACK; 30 import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; 31 32 import android.annotation.Nullable; 33 import android.net.IpPrefix; 34 import android.net.LinkAddress; 35 import android.net.LinkProperties; 36 import android.net.RouteInfo; 37 import android.os.ParcelFileDescriptor; 38 import android.os.SystemClock; 39 import android.system.ErrnoException; 40 import android.system.Os; 41 42 import com.android.net.module.util.HexDump; 43 import com.android.net.module.util.LinkPropertiesUtils.CompareResult; 44 import com.android.net.module.util.SharedLog; 45 import com.android.net.module.util.netlink.NetlinkUtils; 46 import com.android.net.module.util.netlink.StructIfinfoMsg; 47 import com.android.net.module.util.netlink.StructNlAttr; 48 import com.android.net.module.util.netlink.StructNlMsgHdr; 49 import com.android.server.thread.openthread.Ipv6AddressInfo; 50 import com.android.server.thread.openthread.OnMeshPrefixConfig; 51 52 import java.io.IOException; 53 import java.net.Inet6Address; 54 import java.net.InetAddress; 55 import java.net.InetSocketAddress; 56 import java.net.MulticastSocket; 57 import java.net.NetworkInterface; 58 import java.net.SocketException; 59 import java.net.UnknownHostException; 60 import java.nio.ByteBuffer; 61 import java.nio.ByteOrder; 62 import java.util.ArrayList; 63 import java.util.List; 64 65 /** Controller for virtual/tunnel network interfaces. */ 66 public class TunInterfaceController { 67 private static final String TAG = "TunIfController"; 68 private static final boolean DBG = false; 69 private static final SharedLog LOG = ThreadNetworkLogger.forSubComponent(TAG); 70 private static final long INFINITE_LIFETIME = 0xffffffffL; 71 static final int MTU = 1280; 72 73 static { 74 System.loadLibrary("service-thread-jni"); 75 } 76 77 private final String mIfName; 78 private final LinkProperties mLinkProperties = new LinkProperties(); 79 private final MulticastSocket mMulticastSocket; // For join group and leave group 80 private final List<InetAddress> mMulticastAddresses = new ArrayList<>(); 81 private final List<RouteInfo> mNetDataPrefixes = new ArrayList<>(); 82 83 private ParcelFileDescriptor mParcelTunFd; 84 private NetworkInterface mNetworkInterface; 85 86 /** Creates a new {@link TunInterfaceController} instance for given interface. */ TunInterfaceController(String interfaceName)87 public TunInterfaceController(String interfaceName) { 88 mIfName = interfaceName; 89 mLinkProperties.setInterfaceName(mIfName); 90 mLinkProperties.setMtu(MTU); 91 mMulticastSocket = createMulticastSocket(); 92 } 93 94 /** Returns link properties of the Thread TUN interface. */ getLinkProperties()95 private LinkProperties getLinkProperties() { 96 return new LinkProperties(mLinkProperties); 97 } 98 99 /** Returns link properties of the Thread TUN interface with the given NAT64 CIDR. */ 100 // TODO: manage the NAT64 CIDR in the TunInterfaceController getLinkPropertiesWithNat64Cidr(@ullable LinkAddress nat64Cidr)101 public LinkProperties getLinkPropertiesWithNat64Cidr(@Nullable LinkAddress nat64Cidr) { 102 final LinkProperties lp = getLinkProperties(); 103 if (nat64Cidr != null) { 104 lp.addLinkAddress(nat64Cidr); 105 lp.addRoute(getRouteForAddress(nat64Cidr)); 106 } 107 return lp; 108 } 109 110 /** 111 * Creates the tunnel interface. 112 * 113 * @throws IOException if failed to create the interface 114 */ createTunInterface()115 public void createTunInterface() throws IOException { 116 mParcelTunFd = ParcelFileDescriptor.adoptFd(nativeCreateTunInterface(mIfName, MTU)); 117 try { 118 mNetworkInterface = NetworkInterface.getByName(mIfName); 119 } catch (SocketException e) { 120 throw new IOException("Failed to get NetworkInterface", e); 121 } 122 123 setAddrGenModeToNone(); 124 } 125 destroyTunInterface()126 public void destroyTunInterface() { 127 try { 128 mParcelTunFd.close(); 129 } catch (IOException e) { 130 // Should never fail 131 } 132 mParcelTunFd = null; 133 mNetworkInterface = null; 134 } 135 136 /** Returns the FD of the tunnel interface. */ 137 @Nullable getTunFd()138 public ParcelFileDescriptor getTunFd() { 139 return mParcelTunFd; 140 } 141 nativeCreateTunInterface(String interfaceName, int mtu)142 private native int nativeCreateTunInterface(String interfaceName, int mtu) throws IOException; 143 144 /** Sets the interface up or down according to {@code isUp}. */ setInterfaceUp(boolean isUp)145 public void setInterfaceUp(boolean isUp) throws IOException { 146 if (!isUp) { 147 for (LinkAddress address : mLinkProperties.getAllLinkAddresses()) { 148 removeAddress(address); 149 } 150 for (RouteInfo route : mLinkProperties.getAllRoutes()) { 151 mLinkProperties.removeRoute(route); 152 } 153 mNetDataPrefixes.clear(); 154 } 155 nativeSetInterfaceUp(mIfName, isUp); 156 } 157 nativeSetInterfaceUp(String interfaceName, boolean isUp)158 private native void nativeSetInterfaceUp(String interfaceName, boolean isUp) throws IOException; 159 160 /** Adds a new address to the interface. */ addAddress(LinkAddress address)161 public void addAddress(LinkAddress address) { 162 if (!(address.getAddress() instanceof Inet6Address)) { 163 return; 164 } 165 LOG.v("Adding address " + address + " with flags: " + address.getFlags()); 166 167 long preferredLifetimeSeconds; 168 long validLifetimeSeconds; 169 170 if (address.getDeprecationTime() == LinkAddress.LIFETIME_PERMANENT 171 || address.getDeprecationTime() == LinkAddress.LIFETIME_UNKNOWN) { 172 preferredLifetimeSeconds = INFINITE_LIFETIME; 173 } else { 174 preferredLifetimeSeconds = 175 Math.max( 176 (address.getDeprecationTime() - SystemClock.elapsedRealtime()) / 1000L, 177 0L); 178 } 179 180 if (address.getExpirationTime() == LinkAddress.LIFETIME_PERMANENT 181 || address.getExpirationTime() == LinkAddress.LIFETIME_UNKNOWN) { 182 validLifetimeSeconds = INFINITE_LIFETIME; 183 } else { 184 validLifetimeSeconds = 185 Math.max( 186 (address.getExpirationTime() - SystemClock.elapsedRealtime()) / 1000L, 187 0L); 188 } 189 // Only apply to Ipv6 address 190 if (!NetlinkUtils.sendRtmNewAddressRequest( 191 Os.if_nametoindex(mIfName), 192 address.getAddress(), 193 (short) address.getPrefixLength(), 194 address.getFlags(), 195 (byte) address.getScope(), 196 preferredLifetimeSeconds, 197 validLifetimeSeconds)) { 198 LOG.w("Failed to add address " + address.getAddress().getHostAddress()); 199 return; 200 } 201 mLinkProperties.addLinkAddress(address); 202 mLinkProperties.addRoute(getRouteForAddress(address)); 203 } 204 205 /** Removes an address from the interface. */ removeAddress(LinkAddress address)206 public void removeAddress(LinkAddress address) { 207 if (!(address.getAddress() instanceof Inet6Address)) { 208 return; 209 } 210 LOG.v("Removing address " + address); 211 212 // Intentionally update the mLinkProperties before send netlink message because the 213 // address is already removed from ot-daemon and apps can't reach to the address even 214 // when the netlink request below fails 215 mLinkProperties.removeLinkAddress(address); 216 mLinkProperties.removeRoute(getRouteForAddress(address)); 217 // Only apply to Ipv6 address 218 if (!NetlinkUtils.sendRtmDelAddressRequest( 219 Os.if_nametoindex(mIfName), 220 (Inet6Address) address.getAddress(), 221 (short) address.getPrefixLength())) { 222 LOG.w("Failed to remove address " + address.getAddress().getHostAddress()); 223 } 224 } 225 updateAddresses(List<Ipv6AddressInfo> addressInfoList)226 public void updateAddresses(List<Ipv6AddressInfo> addressInfoList) { 227 final List<LinkAddress> newLinkAddresses = new ArrayList<>(); 228 final List<InetAddress> newMulticastAddresses = new ArrayList<>(); 229 boolean hasActiveOmrAddress = false; 230 231 for (Ipv6AddressInfo addressInfo : addressInfoList) { 232 if (addressInfo.isActiveOmr) { 233 hasActiveOmrAddress = true; 234 break; 235 } 236 } 237 238 for (Ipv6AddressInfo addressInfo : addressInfoList) { 239 InetAddress address = addressInfoToInetAddress(addressInfo); 240 if (address.isMulticastAddress()) { 241 newMulticastAddresses.add(address); 242 } else { 243 newLinkAddresses.add(newLinkAddress(addressInfo, hasActiveOmrAddress)); 244 } 245 } 246 247 final CompareResult<LinkAddress> addressDiff = 248 new CompareResult<>(mLinkProperties.getAllLinkAddresses(), newLinkAddresses); 249 for (LinkAddress linkAddress : addressDiff.removed) { 250 removeAddress(linkAddress); 251 } 252 for (LinkAddress linkAddress : addressDiff.added) { 253 addAddress(linkAddress); 254 } 255 256 final CompareResult<InetAddress> multicastAddressDiff = 257 new CompareResult<>(mMulticastAddresses, newMulticastAddresses); 258 for (InetAddress address : multicastAddressDiff.removed) { 259 leaveGroup(address); 260 } 261 for (InetAddress address : multicastAddressDiff.added) { 262 joinGroup(address); 263 } 264 mMulticastAddresses.clear(); 265 mMulticastAddresses.addAll(newMulticastAddresses); 266 } 267 updatePrefixes(List<OnMeshPrefixConfig> onMeshPrefixConfigList)268 public void updatePrefixes(List<OnMeshPrefixConfig> onMeshPrefixConfigList) { 269 final List<RouteInfo> newNetDataPrefixes = new ArrayList<>(); 270 271 for (OnMeshPrefixConfig onMeshPrefixConfig : onMeshPrefixConfigList) { 272 newNetDataPrefixes.add(getRouteForOnMeshPrefix(onMeshPrefixConfig)); 273 } 274 275 final CompareResult<RouteInfo> prefixDiff = 276 new CompareResult<>(mNetDataPrefixes, newNetDataPrefixes); 277 for (RouteInfo routeRemoved : prefixDiff.removed) { 278 mLinkProperties.removeRoute(routeRemoved); 279 } 280 for (RouteInfo routeAdded : prefixDiff.added) { 281 mLinkProperties.addRoute(routeAdded); 282 } 283 284 mNetDataPrefixes.clear(); 285 mNetDataPrefixes.addAll(newNetDataPrefixes); 286 } 287 getRouteForAddress(LinkAddress linkAddress)288 private RouteInfo getRouteForAddress(LinkAddress linkAddress) { 289 return getRouteForIpPrefix( 290 new IpPrefix(linkAddress.getAddress(), linkAddress.getPrefixLength())); 291 } 292 getRouteForOnMeshPrefix(OnMeshPrefixConfig onMeshPrefixConfig)293 private RouteInfo getRouteForOnMeshPrefix(OnMeshPrefixConfig onMeshPrefixConfig) { 294 return getRouteForIpPrefix( 295 new IpPrefix( 296 bytesToInet6Address(onMeshPrefixConfig.prefix), 297 onMeshPrefixConfig.prefixLength)); 298 } 299 getRouteForIpPrefix(IpPrefix ipPrefix)300 private RouteInfo getRouteForIpPrefix(IpPrefix ipPrefix) { 301 return new RouteInfo(ipPrefix, null, mIfName, RouteInfo.RTN_UNICAST, MTU); 302 } 303 304 /** Called by {@link ThreadNetworkControllerService} to do clean up when ot-daemon is dead. */ onOtDaemonDied()305 public void onOtDaemonDied() { 306 try { 307 setInterfaceUp(false); 308 } catch (IOException e) { 309 LOG.e("Failed to set Thread TUN interface down"); 310 } 311 } 312 addressInfoToInetAddress(Ipv6AddressInfo addressInfo)313 private static InetAddress addressInfoToInetAddress(Ipv6AddressInfo addressInfo) { 314 return bytesToInet6Address(addressInfo.address); 315 } 316 bytesToInet6Address(byte[] addressBytes)317 private static Inet6Address bytesToInet6Address(byte[] addressBytes) { 318 try { 319 return (Inet6Address) Inet6Address.getByAddress(addressBytes); 320 } catch (UnknownHostException e) { 321 // This is unlikely to happen unless the Thread daemon is critically broken 322 return null; 323 } 324 } 325 newLinkAddress( Ipv6AddressInfo addressInfo, boolean hasActiveOmrAddress)326 private static LinkAddress newLinkAddress( 327 Ipv6AddressInfo addressInfo, boolean hasActiveOmrAddress) { 328 // Mesh-local addresses and OMR address have the same scope, to distinguish them we set 329 // mesh-local addresses as deprecated when there is an active OMR address. If OMR address 330 // is missing, only ML-EID in mesh-local addresses will be set preferred. 331 // For OMR address and link-local address we only use the value isPreferred set by 332 // ot-daemon. 333 boolean isPreferred = addressInfo.isPreferred; 334 if (addressInfo.isMeshLocal) { 335 isPreferred = (!hasActiveOmrAddress && addressInfo.isMeshLocalEid); 336 } 337 338 final long deprecationTimeMillis = 339 isPreferred ? LinkAddress.LIFETIME_PERMANENT : SystemClock.elapsedRealtime(); 340 341 final InetAddress address = addressInfoToInetAddress(addressInfo); 342 343 // flags and scope will be adjusted automatically depending on the address and 344 // its lifetimes. 345 return new LinkAddress( 346 address, 347 addressInfo.prefixLength, 348 0 /* flags */, 349 0 /* scope */, 350 deprecationTimeMillis, 351 LinkAddress.LIFETIME_PERMANENT /* expirationTime */); 352 } 353 createMulticastSocket()354 private MulticastSocket createMulticastSocket() { 355 try { 356 return new MulticastSocket(); 357 } catch (IOException e) { 358 throw new IllegalStateException("Failed to create multicast socket ", e); 359 } 360 } 361 joinGroup(InetAddress address)362 private void joinGroup(InetAddress address) { 363 InetSocketAddress socketAddress = new InetSocketAddress(address, 0); 364 try { 365 mMulticastSocket.joinGroup(socketAddress, mNetworkInterface); 366 } catch (IOException e) { 367 if (e.getCause() instanceof ErrnoException) { 368 ErrnoException ee = (ErrnoException) e.getCause(); 369 if (ee.errno == EADDRINUSE) { 370 LOG.w( 371 "Already joined group " 372 + address.getHostAddress() 373 + ": " 374 + e.getMessage()); 375 return; 376 } 377 } 378 LOG.e("failed to join group " + address.getHostAddress(), e); 379 } 380 } 381 leaveGroup(InetAddress address)382 private void leaveGroup(InetAddress address) { 383 InetSocketAddress socketAddress = new InetSocketAddress(address, 0); 384 try { 385 mMulticastSocket.leaveGroup(socketAddress, mNetworkInterface); 386 } catch (IOException e) { 387 LOG.e("failed to leave group " + address.getHostAddress(), e); 388 } 389 } 390 391 /** 392 * Sets the address generation mode to {@code IN6_ADDR_GEN_MODE_NONE}. 393 * 394 * <p>So that the "thread-wpan" interface has only one IPv6 link local address which is 395 * generated by OpenThread. 396 */ setAddrGenModeToNone()397 private void setAddrGenModeToNone() { 398 StructNlMsgHdr header = new StructNlMsgHdr(); 399 header.nlmsg_type = RTM_NEWLINK; 400 header.nlmsg_pid = 0; 401 header.nlmsg_seq = 0; 402 header.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; 403 404 StructIfinfoMsg ifInfo = 405 new StructIfinfoMsg( 406 (short) 0 /* family */, 407 0 /* type */, 408 Os.if_nametoindex(mIfName), 409 (IFF_MULTICAST | IFF_NOARP) /* flags */, 410 0xffffffff /* change */); 411 412 // Nested attributes 413 // IFLA_AF_SPEC 414 // AF_INET6 415 // IFLA_INET6_ADDR_GEN_MODE 416 StructNlAttr addrGenMode = 417 new StructNlAttr(IFLA_INET6_ADDR_GEN_MODE, (byte) IN6_ADDR_GEN_MODE_NONE); 418 StructNlAttr afInet6 = new StructNlAttr((short) AF_INET6, addrGenMode); 419 StructNlAttr afSpec = new StructNlAttr(IFLA_AF_SPEC, afInet6); 420 421 final int msgLength = 422 StructNlMsgHdr.STRUCT_SIZE 423 + StructIfinfoMsg.STRUCT_SIZE 424 + afSpec.getAlignedLength(); 425 byte[] msg = new byte[msgLength]; 426 ByteBuffer buf = ByteBuffer.wrap(msg); 427 buf.order(ByteOrder.nativeOrder()); 428 429 header.nlmsg_len = msgLength; 430 header.pack(buf); 431 ifInfo.pack(buf); 432 afSpec.pack(buf); 433 434 if (buf.position() != msgLength) { 435 throw new AssertionError( 436 String.format( 437 "Unexpected netlink message size (actual = %d, expected = %d)", 438 buf.position(), msgLength)); 439 } 440 441 if (DBG) { 442 LOG.v("ADDR_GEN_MODE message is:"); 443 LOG.v(HexDump.dumpHexString(msg)); 444 } 445 446 try { 447 NetlinkUtils.sendOneShotKernelMessage(NETLINK_ROUTE, msg); 448 } catch (ErrnoException e) { 449 LOG.e("Failed to set ADDR_GEN_MODE to NONE", e); 450 } 451 } 452 } 453