1 /* 2 * Copyright (C) 2022 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.connectivity; 18 19 import static android.net.INetd.IF_STATE_UP; 20 import static android.net.INetd.PERMISSION_NETWORK; 21 import static android.net.INetd.PERMISSION_SYSTEM; 22 import static android.system.OsConstants.ETH_P_IP; 23 import static android.system.OsConstants.ETH_P_IPV6; 24 25 import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU; 26 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.net.INetd; 30 import android.net.InetAddresses; 31 import android.net.InterfaceConfigurationParcel; 32 import android.net.IpPrefix; 33 import android.os.ParcelFileDescriptor; 34 import android.os.RemoteException; 35 import android.os.ServiceSpecificException; 36 import android.system.ErrnoException; 37 import android.util.Log; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.util.IndentingPrintWriter; 41 import com.android.modules.utils.build.SdkLevel; 42 import com.android.net.module.util.BpfMap; 43 import com.android.net.module.util.IBpfMap; 44 import com.android.net.module.util.InterfaceParams; 45 import com.android.net.module.util.TcUtils; 46 import com.android.net.module.util.bpf.ClatEgress4Key; 47 import com.android.net.module.util.bpf.ClatEgress4Value; 48 import com.android.net.module.util.bpf.ClatIngress6Key; 49 import com.android.net.module.util.bpf.ClatIngress6Value; 50 51 import java.io.FileDescriptor; 52 import java.io.IOException; 53 import java.net.Inet4Address; 54 import java.net.Inet6Address; 55 import java.net.InetAddress; 56 import java.nio.ByteBuffer; 57 import java.util.Objects; 58 59 /** 60 * This coordinator is responsible for providing clat relevant functionality. 61 * 62 * {@hide} 63 */ 64 public class ClatCoordinator { 65 private static final String TAG = ClatCoordinator.class.getSimpleName(); 66 67 // Sync from external/android-clat/clatd.c 68 // 40 bytes IPv6 header - 20 bytes IPv4 header + 8 bytes fragment header. 69 @VisibleForTesting 70 static final int MTU_DELTA = 28; 71 @VisibleForTesting 72 static final int CLAT_MAX_MTU = 65536; 73 74 // This must match the interface prefix in clatd.c. 75 private static final String CLAT_PREFIX = "v4-"; 76 77 // For historical reasons, start with 192.0.0.4, and after that, use all subsequent addresses 78 // in 192.0.0.0/29 (RFC 7335). 79 @VisibleForTesting 80 static final String INIT_V4ADDR_STRING = "192.0.0.4"; 81 @VisibleForTesting 82 static final int INIT_V4ADDR_PREFIX_LEN = 29; 83 private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8"); 84 85 private static final int INVALID_IFINDEX = 0; 86 87 // For better code clarity when used for 'bool ingress' parameter. 88 @VisibleForTesting 89 static final boolean EGRESS = false; 90 @VisibleForTesting 91 static final boolean INGRESS = true; 92 93 // For better code clarity when used for 'bool ether' parameter. 94 static final boolean RAWIP = false; 95 static final boolean ETHER = true; 96 97 // The priority of clat hook - must be after tethering. 98 @VisibleForTesting 99 static final int PRIO_CLAT = 4; 100 101 private static final String CLAT_EGRESS4_MAP_PATH = makeMapPath("egress4"); 102 private static final String CLAT_INGRESS6_MAP_PATH = makeMapPath("ingress6"); 103 makeMapPath(String which)104 private static String makeMapPath(String which) { 105 return "/sys/fs/bpf/net_shared/map_clatd_clat_" + which + "_map"; 106 } 107 makeProgPath(boolean ingress, boolean ether)108 private static String makeProgPath(boolean ingress, boolean ether) { 109 String path = "/sys/fs/bpf/net_shared/prog_clatd_schedcls_" 110 + (ingress ? "ingress6" : "egress4") 111 + "_clat_" 112 + (ether ? "ether" : "rawip"); 113 return path; 114 } 115 116 @NonNull 117 private final INetd mNetd; 118 @NonNull 119 private final Dependencies mDeps; 120 @Nullable 121 private final IBpfMap<ClatIngress6Key, ClatIngress6Value> mIngressMap; 122 @Nullable 123 private final IBpfMap<ClatEgress4Key, ClatEgress4Value> mEgressMap; 124 @Nullable 125 private ClatdTracker mClatdTracker = null; 126 127 /** 128 * Dependencies of ClatCoordinator which makes ConnectivityService injection 129 * in tests. 130 */ 131 @VisibleForTesting 132 public abstract static class Dependencies { 133 /** 134 * Get netd. 135 */ 136 @NonNull getNetd()137 public abstract INetd getNetd(); 138 139 /** 140 * @see ParcelFileDescriptor#adoptFd(int). 141 */ 142 @NonNull adoptFd(int fd)143 public ParcelFileDescriptor adoptFd(int fd) { 144 return ParcelFileDescriptor.adoptFd(fd); 145 } 146 147 /** 148 * Get interface index for a given interface. 149 */ getInterfaceIndex(String ifName)150 public int getInterfaceIndex(String ifName) { 151 final InterfaceParams params = InterfaceParams.getByName(ifName); 152 return params != null ? params.index : INVALID_IFINDEX; 153 } 154 155 /** 156 * Create tun interface for a given interface name. 157 */ createTunInterface(@onNull String tuniface)158 public int createTunInterface(@NonNull String tuniface) throws IOException { 159 return native_createTunInterface(tuniface); 160 } 161 162 /** 163 * Pick an IPv4 address for clat. 164 */ 165 @NonNull selectIpv4Address(@onNull String v4addr, int prefixlen)166 public String selectIpv4Address(@NonNull String v4addr, int prefixlen) 167 throws IOException { 168 return native_selectIpv4Address(v4addr, prefixlen); 169 } 170 171 /** 172 * Generate a checksum-neutral IID. 173 */ 174 @NonNull generateIpv6Address(@onNull String iface, @NonNull String v4, @NonNull String prefix64)175 public String generateIpv6Address(@NonNull String iface, @NonNull String v4, 176 @NonNull String prefix64) throws IOException { 177 return native_generateIpv6Address(iface, v4, prefix64); 178 } 179 180 /** 181 * Detect MTU. 182 */ detectMtu(@onNull String platSubnet, int platSuffix, int mark)183 public int detectMtu(@NonNull String platSubnet, int platSuffix, int mark) 184 throws IOException { 185 return native_detectMtu(platSubnet, platSuffix, mark); 186 } 187 188 /** 189 * Open packet socket. 190 */ openPacketSocket()191 public int openPacketSocket() throws IOException { 192 return native_openPacketSocket(); 193 } 194 195 /** 196 * Open IPv6 raw socket and set SO_MARK. 197 */ openRawSocket6(int mark)198 public int openRawSocket6(int mark) throws IOException { 199 return native_openRawSocket6(mark); 200 } 201 202 /** 203 * Add anycast setsockopt. 204 */ addAnycastSetsockopt(@onNull FileDescriptor sock, String v6, int ifindex)205 public void addAnycastSetsockopt(@NonNull FileDescriptor sock, String v6, int ifindex) 206 throws IOException { 207 native_addAnycastSetsockopt(sock, v6, ifindex); 208 } 209 210 /** 211 * Configure packet socket. 212 */ configurePacketSocket(@onNull FileDescriptor sock, String v6, int ifindex)213 public void configurePacketSocket(@NonNull FileDescriptor sock, String v6, int ifindex) 214 throws IOException { 215 native_configurePacketSocket(sock, v6, ifindex); 216 } 217 218 /** 219 * Start clatd. 220 */ startClatd(@onNull FileDescriptor tunfd, @NonNull FileDescriptor readsock6, @NonNull FileDescriptor writesock6, @NonNull String iface, @NonNull String pfx96, @NonNull String v4, @NonNull String v6)221 public int startClatd(@NonNull FileDescriptor tunfd, @NonNull FileDescriptor readsock6, 222 @NonNull FileDescriptor writesock6, @NonNull String iface, @NonNull String pfx96, 223 @NonNull String v4, @NonNull String v6) throws IOException { 224 return native_startClatd(tunfd, readsock6, writesock6, iface, pfx96, v4, v6); 225 } 226 227 /** 228 * Stop clatd. 229 */ stopClatd(String iface, String pfx96, String v4, String v6, int pid)230 public void stopClatd(String iface, String pfx96, String v4, String v6, int pid) 231 throws IOException { 232 native_stopClatd(iface, pfx96, v4, v6, pid); 233 } 234 235 /** 236 * Tag socket as clat. 237 */ tagSocketAsClat(@onNull FileDescriptor sock)238 public long tagSocketAsClat(@NonNull FileDescriptor sock) throws IOException { 239 return native_tagSocketAsClat(sock); 240 } 241 242 /** 243 * Untag socket. 244 */ untagSocket(long cookie)245 public void untagSocket(long cookie) throws IOException { 246 native_untagSocket(cookie); 247 } 248 249 /** Get ingress6 BPF map. */ 250 @Nullable getBpfIngress6Map()251 public IBpfMap<ClatIngress6Key, ClatIngress6Value> getBpfIngress6Map() { 252 // Pre-T devices don't use ClatCoordinator to access clat map. Since Nat464Xlat 253 // initializes a ClatCoordinator object to avoid redundant null pointer check 254 // while using, ignore the BPF map initialization on pre-T devices. 255 // TODO: probably don't initialize ClatCoordinator object on pre-T devices. 256 if (!SdkLevel.isAtLeastT()) return null; 257 try { 258 return new BpfMap<>(CLAT_INGRESS6_MAP_PATH, 259 BpfMap.BPF_F_RDWR, ClatIngress6Key.class, ClatIngress6Value.class); 260 } catch (ErrnoException e) { 261 Log.e(TAG, "Cannot create ingress6 map: " + e); 262 return null; 263 } 264 } 265 266 /** Get egress4 BPF map. */ 267 @Nullable getBpfEgress4Map()268 public IBpfMap<ClatEgress4Key, ClatEgress4Value> getBpfEgress4Map() { 269 // Pre-T devices don't use ClatCoordinator to access clat map. Since Nat464Xlat 270 // initializes a ClatCoordinator object to avoid redundant null pointer check 271 // while using, ignore the BPF map initialization on pre-T devices. 272 // TODO: probably don't initialize ClatCoordinator object on pre-T devices. 273 if (!SdkLevel.isAtLeastT()) return null; 274 try { 275 return new BpfMap<>(CLAT_EGRESS4_MAP_PATH, 276 BpfMap.BPF_F_RDWR, ClatEgress4Key.class, ClatEgress4Value.class); 277 } catch (ErrnoException e) { 278 Log.e(TAG, "Cannot create egress4 map: " + e); 279 return null; 280 } 281 } 282 283 /** Checks if the network interface uses an ethernet L2 header. */ isEthernet(String iface)284 public boolean isEthernet(String iface) throws IOException { 285 return TcUtils.isEthernet(iface); 286 } 287 288 /** Add a clsact qdisc. */ tcQdiscAddDevClsact(int ifIndex)289 public void tcQdiscAddDevClsact(int ifIndex) throws IOException { 290 TcUtils.tcQdiscAddDevClsact(ifIndex); 291 } 292 293 /** Attach a tc bpf filter. */ tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio, short proto, String bpfProgPath)294 public void tcFilterAddDevBpf(int ifIndex, boolean ingress, short prio, short proto, 295 String bpfProgPath) throws IOException { 296 TcUtils.tcFilterAddDevBpf(ifIndex, ingress, prio, proto, bpfProgPath); 297 } 298 299 /** Delete a tc filter. */ tcFilterDelDev(int ifIndex, boolean ingress, short prio, short proto)300 public void tcFilterDelDev(int ifIndex, boolean ingress, short prio, short proto) 301 throws IOException { 302 TcUtils.tcFilterDelDev(ifIndex, ingress, prio, proto); 303 } 304 } 305 306 @VisibleForTesting 307 static class ClatdTracker { 308 @NonNull 309 public final String iface; 310 public final int ifIndex; 311 @NonNull 312 public final String v4iface; 313 public final int v4ifIndex; 314 @NonNull 315 public final Inet4Address v4; 316 @NonNull 317 public final Inet6Address v6; 318 @NonNull 319 public final Inet6Address pfx96; 320 public final int pid; 321 public final long cookie; 322 ClatdTracker(@onNull String iface, int ifIndex, @NonNull String v4iface, int v4ifIndex, @NonNull Inet4Address v4, @NonNull Inet6Address v6, @NonNull Inet6Address pfx96, int pid, long cookie)323 ClatdTracker(@NonNull String iface, int ifIndex, @NonNull String v4iface, 324 int v4ifIndex, @NonNull Inet4Address v4, @NonNull Inet6Address v6, 325 @NonNull Inet6Address pfx96, int pid, long cookie) { 326 this.iface = iface; 327 this.ifIndex = ifIndex; 328 this.v4iface = v4iface; 329 this.v4ifIndex = v4ifIndex; 330 this.v4 = v4; 331 this.v6 = v6; 332 this.pfx96 = pfx96; 333 this.pid = pid; 334 this.cookie = cookie; 335 } 336 337 @Override equals(Object o)338 public boolean equals(Object o) { 339 if (!(o instanceof ClatdTracker)) return false; 340 ClatdTracker that = (ClatdTracker) o; 341 return Objects.equals(this.iface, that.iface) 342 && this.ifIndex == that.ifIndex 343 && Objects.equals(this.v4iface, that.v4iface) 344 && this.v4ifIndex == that.v4ifIndex 345 && Objects.equals(this.v4, that.v4) 346 && Objects.equals(this.v6, that.v6) 347 && Objects.equals(this.pfx96, that.pfx96) 348 && this.pid == that.pid 349 && this.cookie == that.cookie; 350 } 351 }; 352 353 @VisibleForTesting getFwmark(int netId)354 static int getFwmark(int netId) { 355 // See union Fwmark in system/netd/include/Fwmark.h 356 return (netId & 0xffff) 357 | 0x1 << 16 // explicitlySelected: true 358 | 0x1 << 17 // protectedFromVpn: true 359 | ((PERMISSION_NETWORK | PERMISSION_SYSTEM) & 0x3) << 18; // 2 permission bits = 3 360 } 361 362 @VisibleForTesting adjustMtu(int mtu)363 static int adjustMtu(int mtu) { 364 // clamp to minimum ipv6 mtu - this probably cannot ever trigger 365 if (mtu < IPV6_MIN_MTU) mtu = IPV6_MIN_MTU; 366 // clamp to buffer size 367 if (mtu > CLAT_MAX_MTU) mtu = CLAT_MAX_MTU; 368 // decrease by ipv6(40) + ipv6 fragmentation header(8) vs ipv4(20) overhead of 28 bytes 369 mtu -= MTU_DELTA; 370 371 return mtu; 372 } 373 ClatCoordinator(@onNull Dependencies deps)374 public ClatCoordinator(@NonNull Dependencies deps) { 375 mDeps = deps; 376 mNetd = mDeps.getNetd(); 377 mIngressMap = mDeps.getBpfIngress6Map(); 378 mEgressMap = mDeps.getBpfEgress4Map(); 379 } 380 maybeStartBpf(final ClatdTracker tracker)381 private void maybeStartBpf(final ClatdTracker tracker) { 382 if (mIngressMap == null || mEgressMap == null) return; 383 384 final boolean isEthernet; 385 try { 386 isEthernet = mDeps.isEthernet(tracker.iface); 387 } catch (IOException e) { 388 Log.e(TAG, "Fail to call isEthernet for interface " + tracker.iface); 389 return; 390 } 391 392 final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4); 393 final ClatEgress4Value txValue = new ClatEgress4Value(tracker.ifIndex, tracker.v6, 394 tracker.pfx96, (short) (isEthernet ? 1 /* ETHER */ : 0 /* RAWIP */)); 395 try { 396 mEgressMap.insertEntry(txKey, txValue); 397 } catch (ErrnoException | IllegalStateException e) { 398 Log.e(TAG, "Could not insert entry (" + txKey + ", " + txValue + ") on egress map: " 399 + e); 400 return; 401 } 402 403 final ClatIngress6Key rxKey = new ClatIngress6Key(tracker.ifIndex, tracker.pfx96, 404 tracker.v6); 405 final ClatIngress6Value rxValue = new ClatIngress6Value(tracker.v4ifIndex, 406 tracker.v4); 407 try { 408 mIngressMap.insertEntry(rxKey, rxValue); 409 } catch (ErrnoException | IllegalStateException e) { 410 Log.e(TAG, "Could not insert entry (" + rxKey + ", " + rxValue + ") ingress map: " 411 + e); 412 try { 413 mEgressMap.deleteEntry(txKey); 414 } catch (ErrnoException | IllegalStateException e2) { 415 Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2); 416 } 417 return; 418 } 419 420 // Usually the clsact will be added in netd RouteController::addInterfaceToPhysicalNetwork. 421 // But clat is started before the v4- interface is added to the network. The clat startup 422 // have to add clsact of v4- tun interface first for adding bpf filter in maybeStartBpf. 423 try { 424 // tc qdisc add dev .. clsact 425 mDeps.tcQdiscAddDevClsact(tracker.v4ifIndex); 426 } catch (IOException e) { 427 Log.e(TAG, "tc qdisc add dev (" + tracker.v4ifIndex + "[" + tracker.v4iface 428 + "]) failure: " + e); 429 try { 430 mEgressMap.deleteEntry(txKey); 431 } catch (ErrnoException | IllegalStateException e2) { 432 Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2); 433 } 434 try { 435 mIngressMap.deleteEntry(rxKey); 436 } catch (ErrnoException | IllegalStateException e3) { 437 Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e3); 438 } 439 return; 440 } 441 442 // This program will be attached to the v4-* interface which is a TUN and thus always rawip. 443 try { 444 // tc filter add dev .. egress prio 4 protocol ip bpf object-pinned /sys/fs/bpf/... 445 // direct-action 446 mDeps.tcFilterAddDevBpf(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, (short) ETH_P_IP, 447 makeProgPath(EGRESS, RAWIP)); 448 } catch (IOException e) { 449 Log.e(TAG, "tc filter add dev (" + tracker.v4ifIndex + "[" + tracker.v4iface 450 + "]) egress prio PRIO_CLAT protocol ip failure: " + e); 451 452 // The v4- interface clsact is not deleted for unwinding error because once it is 453 // created with interface addition, the lifetime is till interface deletion. Moreover, 454 // the clsact has no clat filter now. It should not break anything. 455 456 try { 457 mEgressMap.deleteEntry(txKey); 458 } catch (ErrnoException | IllegalStateException e2) { 459 Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e2); 460 } 461 try { 462 mIngressMap.deleteEntry(rxKey); 463 } catch (ErrnoException | IllegalStateException e3) { 464 Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e3); 465 } 466 return; 467 } 468 469 try { 470 // tc filter add dev .. ingress prio 4 protocol ipv6 bpf object-pinned /sys/fs/bpf/... 471 // direct-action 472 mDeps.tcFilterAddDevBpf(tracker.ifIndex, INGRESS, (short) PRIO_CLAT, 473 (short) ETH_P_IPV6, makeProgPath(INGRESS, isEthernet)); 474 } catch (IOException e) { 475 Log.e(TAG, "tc filter add dev (" + tracker.ifIndex + "[" + tracker.iface 476 + "]) ingress prio PRIO_CLAT protocol ipv6 failure: " + e); 477 478 // The v4- interface clsact is not deleted. See the reason in the error unwinding code 479 // of the egress filter attaching of v4- tun interface. 480 481 try { 482 mDeps.tcFilterDelDev(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, 483 (short) ETH_P_IP); 484 } catch (IOException e2) { 485 Log.e(TAG, "tc filter del dev (" + tracker.v4ifIndex + "[" + tracker.v4iface 486 + "]) egress prio PRIO_CLAT protocol ip failure: " + e2); 487 } 488 try { 489 mEgressMap.deleteEntry(txKey); 490 } catch (ErrnoException | IllegalStateException e3) { 491 Log.e(TAG, "Could not delete entry (" + txKey + ") from egress map: " + e3); 492 } 493 try { 494 mIngressMap.deleteEntry(rxKey); 495 } catch (ErrnoException | IllegalStateException e4) { 496 Log.e(TAG, "Could not delete entry (" + rxKey + ") from ingress map: " + e4); 497 } 498 return; 499 } 500 } 501 502 /** 503 * Start clatd for a given interface and NAT64 prefix. 504 */ clatStart(final String iface, final int netId, @NonNull final IpPrefix nat64Prefix)505 public String clatStart(final String iface, final int netId, 506 @NonNull final IpPrefix nat64Prefix) 507 throws IOException { 508 if (mClatdTracker != null) { 509 throw new IOException("Clatd is already running on " + mClatdTracker.iface 510 + " (pid " + mClatdTracker.pid + ")"); 511 } 512 if (nat64Prefix.getPrefixLength() != 96) { 513 throw new IOException("Prefix must be 96 bits long: " + nat64Prefix); 514 } 515 516 // [1] Pick an IPv4 address from 192.0.0.4, 192.0.0.5, 192.0.0.6 .. 517 final String v4Str; 518 try { 519 v4Str = mDeps.selectIpv4Address(INIT_V4ADDR_STRING, INIT_V4ADDR_PREFIX_LEN); 520 } catch (IOException e) { 521 throw new IOException("no IPv4 addresses were available for clat: " + e); 522 } 523 524 final Inet4Address v4; 525 try { 526 v4 = (Inet4Address) InetAddresses.parseNumericAddress(v4Str); 527 } catch (ClassCastException | IllegalArgumentException | NullPointerException e) { 528 throw new IOException("Invalid IPv4 address " + v4Str); 529 } 530 531 // [2] Generate a checksum-neutral IID. 532 final String pfx96Str = nat64Prefix.getAddress().getHostAddress(); 533 final String v6Str; 534 try { 535 v6Str = mDeps.generateIpv6Address(iface, v4Str, pfx96Str); 536 } catch (IOException e) { 537 throw new IOException("no IPv6 addresses were available for clat: " + e); 538 } 539 540 final Inet6Address pfx96 = (Inet6Address) nat64Prefix.getAddress(); 541 final Inet6Address v6; 542 try { 543 v6 = (Inet6Address) InetAddresses.parseNumericAddress(v6Str); 544 } catch (ClassCastException | IllegalArgumentException | NullPointerException e) { 545 throw new IOException("Invalid IPv6 address " + v6Str); 546 } 547 548 // [3] Open, configure and bring up the tun interface. 549 // Create the v4-... tun interface. 550 final String tunIface = CLAT_PREFIX + iface; 551 final ParcelFileDescriptor tunFd; 552 try { 553 tunFd = mDeps.adoptFd(mDeps.createTunInterface(tunIface)); 554 } catch (IOException e) { 555 throw new IOException("Create tun interface " + tunIface + " failed: " + e); 556 } 557 558 final int tunIfIndex = mDeps.getInterfaceIndex(tunIface); 559 if (tunIfIndex == INVALID_IFINDEX) { 560 tunFd.close(); 561 throw new IOException("Fail to get interface index for interface " + tunIface); 562 } 563 564 // disable IPv6 on it - failing to do so is not a critical error 565 try { 566 mNetd.interfaceSetEnableIPv6(tunIface, false /* enabled */); 567 } catch (RemoteException | ServiceSpecificException e) { 568 tunFd.close(); 569 Log.e(TAG, "Disable IPv6 on " + tunIface + " failed: " + e); 570 } 571 572 // Detect ipv4 mtu. 573 final Integer fwmark = getFwmark(netId); 574 final int detectedMtu = mDeps.detectMtu(pfx96Str, 575 ByteBuffer.wrap(GOOGLE_DNS_4.getAddress()).getInt(), fwmark); 576 final int mtu = adjustMtu(detectedMtu); 577 Log.i(TAG, "ipv4 mtu is " + mtu); 578 579 // TODO: add setIptablesDropRule 580 581 // Config tun interface mtu, address and bring up. 582 try { 583 mNetd.interfaceSetMtu(tunIface, mtu); 584 } catch (RemoteException | ServiceSpecificException e) { 585 tunFd.close(); 586 throw new IOException("Set MTU " + mtu + " on " + tunIface + " failed: " + e); 587 } 588 final InterfaceConfigurationParcel ifConfig = new InterfaceConfigurationParcel(); 589 ifConfig.ifName = tunIface; 590 ifConfig.ipv4Addr = v4Str; 591 ifConfig.prefixLength = 32; 592 ifConfig.hwAddr = ""; 593 ifConfig.flags = new String[] {IF_STATE_UP}; 594 try { 595 mNetd.interfaceSetCfg(ifConfig); 596 } catch (RemoteException | ServiceSpecificException e) { 597 tunFd.close(); 598 throw new IOException("Setting IPv4 address to " + ifConfig.ipv4Addr + "/" 599 + ifConfig.prefixLength + " failed on " + ifConfig.ifName + ": " + e); 600 } 601 602 // [4] Open and configure local 464xlat read/write sockets. 603 // Opens a packet socket to receive IPv6 packets in clatd. 604 final ParcelFileDescriptor readSock6; 605 try { 606 // Use a JNI call to get native file descriptor instead of Os.socket() because we would 607 // like to use ParcelFileDescriptor to manage file descriptor. But ctor 608 // ParcelFileDescriptor(FileDescriptor fd) is a @hide function. Need to use native file 609 // descriptor to initialize ParcelFileDescriptor object instead. 610 readSock6 = mDeps.adoptFd(mDeps.openPacketSocket()); 611 } catch (IOException e) { 612 tunFd.close(); 613 throw new IOException("Open packet socket failed: " + e); 614 } 615 616 // Opens a raw socket with a given fwmark to send IPv6 packets in clatd. 617 final ParcelFileDescriptor writeSock6; 618 try { 619 // Use a JNI call to get native file descriptor instead of Os.socket(). See above 620 // reason why we use jniOpenPacketSocket6(). 621 writeSock6 = mDeps.adoptFd(mDeps.openRawSocket6(fwmark)); 622 } catch (IOException e) { 623 tunFd.close(); 624 readSock6.close(); 625 throw new IOException("Open raw socket failed: " + e); 626 } 627 628 final int ifIndex = mDeps.getInterfaceIndex(iface); 629 if (ifIndex == INVALID_IFINDEX) { 630 tunFd.close(); 631 readSock6.close(); 632 writeSock6.close(); 633 throw new IOException("Fail to get interface index for interface " + iface); 634 } 635 636 // Start translating packets to the new prefix. 637 try { 638 mDeps.addAnycastSetsockopt(writeSock6.getFileDescriptor(), v6Str, ifIndex); 639 } catch (IOException e) { 640 tunFd.close(); 641 readSock6.close(); 642 writeSock6.close(); 643 throw new IOException("add anycast sockopt failed: " + e); 644 } 645 646 // Tag socket as AID_CLAT to avoid duplicated CLAT data usage accounting. 647 final long cookie; 648 try { 649 cookie = mDeps.tagSocketAsClat(writeSock6.getFileDescriptor()); 650 } catch (IOException e) { 651 tunFd.close(); 652 readSock6.close(); 653 writeSock6.close(); 654 throw new IOException("tag raw socket failed: " + e); 655 } 656 657 // Update our packet socket filter to reflect the new 464xlat IP address. 658 try { 659 mDeps.configurePacketSocket(readSock6.getFileDescriptor(), v6Str, ifIndex); 660 } catch (IOException e) { 661 tunFd.close(); 662 readSock6.close(); 663 writeSock6.close(); 664 throw new IOException("configure packet socket failed: " + e); 665 } 666 667 // [5] Start clatd. 668 final int pid; 669 try { 670 pid = mDeps.startClatd(tunFd.getFileDescriptor(), readSock6.getFileDescriptor(), 671 writeSock6.getFileDescriptor(), iface, pfx96Str, v4Str, v6Str); 672 } catch (IOException e) { 673 // TODO: probably refactor to handle the exception of #untagSocket if any. 674 mDeps.untagSocket(cookie); 675 throw new IOException("Error start clatd on " + iface + ": " + e); 676 } finally { 677 tunFd.close(); 678 readSock6.close(); 679 writeSock6.close(); 680 } 681 682 // [6] Initialize and store clatd tracker object. 683 mClatdTracker = new ClatdTracker(iface, ifIndex, tunIface, tunIfIndex, v4, v6, pfx96, 684 pid, cookie); 685 686 // [7] Start BPF 687 maybeStartBpf(mClatdTracker); 688 689 return v6Str; 690 } 691 maybeStopBpf(final ClatdTracker tracker)692 private void maybeStopBpf(final ClatdTracker tracker) { 693 if (mIngressMap == null || mEgressMap == null) return; 694 695 try { 696 mDeps.tcFilterDelDev(tracker.ifIndex, INGRESS, (short) PRIO_CLAT, (short) ETH_P_IPV6); 697 } catch (IOException e) { 698 Log.e(TAG, "tc filter del dev (" + tracker.ifIndex + "[" + tracker.iface 699 + "]) ingress prio PRIO_CLAT protocol ipv6 failure: " + e); 700 } 701 702 try { 703 mDeps.tcFilterDelDev(tracker.v4ifIndex, EGRESS, (short) PRIO_CLAT, (short) ETH_P_IP); 704 } catch (IOException e) { 705 Log.e(TAG, "tc filter del dev (" + tracker.v4ifIndex + "[" + tracker.v4iface 706 + "]) egress prio PRIO_CLAT protocol ip failure: " + e); 707 } 708 709 // We cleanup the maps last, so scanning through them can be used to 710 // determine what still needs cleanup. 711 712 final ClatEgress4Key txKey = new ClatEgress4Key(tracker.v4ifIndex, tracker.v4); 713 try { 714 mEgressMap.deleteEntry(txKey); 715 } catch (ErrnoException | IllegalStateException e) { 716 Log.e(TAG, "Could not delete entry (" + txKey + "): " + e); 717 } 718 719 final ClatIngress6Key rxKey = new ClatIngress6Key(tracker.ifIndex, tracker.pfx96, 720 tracker.v6); 721 try { 722 mIngressMap.deleteEntry(rxKey); 723 } catch (ErrnoException | IllegalStateException e) { 724 Log.e(TAG, "Could not delete entry (" + rxKey + "): " + e); 725 } 726 } 727 728 /** 729 * Stop clatd 730 */ clatStop()731 public void clatStop() throws IOException { 732 if (mClatdTracker == null) { 733 throw new IOException("Clatd has not started"); 734 } 735 Log.i(TAG, "Stopping clatd pid=" + mClatdTracker.pid + " on " + mClatdTracker.iface); 736 737 maybeStopBpf(mClatdTracker); 738 mDeps.stopClatd(mClatdTracker.iface, mClatdTracker.pfx96.getHostAddress(), 739 mClatdTracker.v4.getHostAddress(), mClatdTracker.v6.getHostAddress(), 740 mClatdTracker.pid); 741 mDeps.untagSocket(mClatdTracker.cookie); 742 743 Log.i(TAG, "clatd on " + mClatdTracker.iface + " stopped"); 744 mClatdTracker = null; 745 } 746 dumpBpfIngress(@onNull IndentingPrintWriter pw)747 private void dumpBpfIngress(@NonNull IndentingPrintWriter pw) { 748 if (mIngressMap == null) { 749 pw.println("No BPF ingress6 map"); 750 return; 751 } 752 753 try { 754 if (mIngressMap.isEmpty()) { 755 pw.println("<empty>"); 756 } 757 pw.println("BPF ingress map: iif nat64Prefix v6Addr -> v4Addr oif"); 758 pw.increaseIndent(); 759 mIngressMap.forEach((k, v) -> { 760 // TODO: print interface name 761 pw.println(String.format("%d %s/96 %s -> %s %d", k.iif, k.pfx96, k.local6, 762 v.local4, v.oif)); 763 }); 764 pw.decreaseIndent(); 765 } catch (ErrnoException e) { 766 pw.println("Error dumping BPF ingress6 map: " + e); 767 } 768 } 769 dumpBpfEgress(@onNull IndentingPrintWriter pw)770 private void dumpBpfEgress(@NonNull IndentingPrintWriter pw) { 771 if (mEgressMap == null) { 772 pw.println("No BPF egress4 map"); 773 return; 774 } 775 776 try { 777 if (mEgressMap.isEmpty()) { 778 pw.println("<empty>"); 779 } 780 pw.println("BPF egress map: iif v4Addr -> v6Addr nat64Prefix oif"); 781 pw.increaseIndent(); 782 mEgressMap.forEach((k, v) -> { 783 // TODO: print interface name 784 pw.println(String.format("%d %s -> %s %s/96 %d %s", k.iif, k.local4, v.local6, 785 v.pfx96, v.oif, v.oifIsEthernet != 0 ? "ether" : "rawip")); 786 }); 787 pw.decreaseIndent(); 788 } catch (ErrnoException e) { 789 pw.println("Error dumping BPF egress4 map: " + e); 790 } 791 } 792 793 /** 794 * Dump the cordinator information. 795 * 796 * @param pw print writer. 797 */ dump(@onNull IndentingPrintWriter pw)798 public void dump(@NonNull IndentingPrintWriter pw) { 799 // TODO: dump ClatdTracker 800 // TODO: move map dump to a global place to avoid duplicate dump while there are two or 801 // more IPv6 only networks. 802 pw.println("Forwarding rules:"); 803 pw.increaseIndent(); 804 dumpBpfIngress(pw); 805 dumpBpfEgress(pw); 806 pw.decreaseIndent(); 807 pw.println(); 808 } 809 810 /** 811 * Get clatd tracker. For test only. 812 */ 813 @VisibleForTesting 814 @Nullable getClatdTrackerForTesting()815 ClatdTracker getClatdTrackerForTesting() { 816 return mClatdTracker; 817 } 818 native_selectIpv4Address(String v4addr, int prefixlen)819 private static native String native_selectIpv4Address(String v4addr, int prefixlen) 820 throws IOException; native_generateIpv6Address(String iface, String v4, String prefix64)821 private static native String native_generateIpv6Address(String iface, String v4, 822 String prefix64) throws IOException; native_createTunInterface(String tuniface)823 private static native int native_createTunInterface(String tuniface) throws IOException; native_detectMtu(String platSubnet, int platSuffix, int mark)824 private static native int native_detectMtu(String platSubnet, int platSuffix, int mark) 825 throws IOException; native_openPacketSocket()826 private static native int native_openPacketSocket() throws IOException; native_openRawSocket6(int mark)827 private static native int native_openRawSocket6(int mark) throws IOException; native_addAnycastSetsockopt(FileDescriptor sock, String v6, int ifindex)828 private static native void native_addAnycastSetsockopt(FileDescriptor sock, String v6, 829 int ifindex) throws IOException; native_configurePacketSocket(FileDescriptor sock, String v6, int ifindex)830 private static native void native_configurePacketSocket(FileDescriptor sock, String v6, 831 int ifindex) throws IOException; native_startClatd(FileDescriptor tunfd, FileDescriptor readsock6, FileDescriptor writesock6, String iface, String pfx96, String v4, String v6)832 private static native int native_startClatd(FileDescriptor tunfd, FileDescriptor readsock6, 833 FileDescriptor writesock6, String iface, String pfx96, String v4, String v6) 834 throws IOException; native_stopClatd(String iface, String pfx96, String v4, String v6, int pid)835 private static native void native_stopClatd(String iface, String pfx96, String v4, String v6, 836 int pid) throws IOException; native_tagSocketAsClat(FileDescriptor sock)837 private static native long native_tagSocketAsClat(FileDescriptor sock) throws IOException; native_untagSocket(long cookie)838 private static native void native_untagSocket(long cookie) throws IOException; 839 } 840