diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java index de537871a7..659d344acf 100644 --- a/Tethering/src/android/net/ip/IpServer.java +++ b/Tethering/src/android/net/ip/IpServer.java @@ -738,7 +738,7 @@ public class IpServer extends StateMachine { // // TODO: Evaluate using a data structure than is more directly suited to // communicating only the relevant information. - private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) { + private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) { if (mRaDaemon == null) return; // Avoid unnecessary work on spurious updates. @@ -761,7 +761,7 @@ public class IpServer extends StateMachine { params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); - if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); + if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment); for (LinkAddress linkAddr : v6only.getLinkAddresses()) { if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; @@ -1052,12 +1052,11 @@ public class IpServer extends StateMachine { } } - private byte getHopLimit(String upstreamIface) { + private byte getHopLimit(String upstreamIface, int adjustTTL) { try { int upstreamHopLimit = Integer.parseUnsignedInt( mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); - // Add one hop to account for this forwarding device - upstreamHopLimit++; + upstreamHopLimit = upstreamHopLimit + adjustTTL; // Cap the hop limit to 255. return (byte) Integer.min(upstreamHopLimit, 255); } catch (Exception e) { @@ -1145,7 +1144,7 @@ public class IpServer extends StateMachine { transitionTo(mUnavailableState); break; case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); + updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); break; default: return NOT_HANDLED; @@ -1209,7 +1208,7 @@ public class IpServer extends StateMachine { if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); break; case CMD_IPV6_TETHER_UPDATE: - updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); + updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1); sendLinkProperties(); break; case CMD_IP_FORWARDING_ENABLE_ERROR: diff --git a/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java index d450c46de7..f3dcaa2529 100644 --- a/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java +++ b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java @@ -161,11 +161,28 @@ public class IPv6TetheringCoordinator { private void updateIPv6TetheringInterfaces() { for (IpServer ipServer : mNotifyList) { final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); - ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, lp); + ipServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, getTtlAdjustment(), 0, lp); break; } } + private int getTtlAdjustment() { + if (mUpstreamNetworkState == null || mUpstreamNetworkState.networkCapabilities == null) { + return 0; + } + + // If upstream is cellular, set the TTL in Router Advertisements to "network-set TTL" - 1 + // for carrier requirement. + if (mUpstreamNetworkState.networkCapabilities.hasTransport( + NetworkCapabilities.TRANSPORT_CELLULAR)) { + return -1; + } + + // For other non-cellular upstream, set TTL as "network-set TTL" + 1 to preventing arbitrary + // distinction between tethered and untethered traffic. + return 1; + } + private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) { final Downstream ds = findDownstream(ipServer); if (ds == null) return null; diff --git a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java index cd1ff607b4..307ebf17d2 100644 --- a/Tethering/tests/unit/src/android/net/ip/IpServerTest.java +++ b/Tethering/tests/unit/src/android/net/ip/IpServerTest.java @@ -17,6 +17,7 @@ package android.net.ip; import static android.net.INetd.IF_STATE_UP; +import static android.net.RouteInfo.RTN_UNICAST; import static android.net.TetheringManager.TETHERING_BLUETOOTH; import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_USB; @@ -74,6 +75,7 @@ import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServerCallbacks; import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; +import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; import android.net.util.InterfaceSet; import android.net.util.SharedLog; @@ -196,7 +198,7 @@ public class IpServerTest { if (upstreamIface != null) { LinkProperties lp = new LinkProperties(); lp.setInterfaceName(upstreamIface); - dispatchTetherConnectionChanged(upstreamIface, lp); + dispatchTetherConnectionChanged(upstreamIface, lp, 0); } reset(mNetd, mCallback); } @@ -694,7 +696,7 @@ public class IpServerTest { InOrder inOrder = inOrder(mNetd); LinkProperties lp = new LinkProperties(); lp.setInterfaceName(UPSTREAM_IFACE2); - dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp, -1); inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); @@ -702,7 +704,7 @@ public class IpServerTest { reset(mNetd); // When the upstream is lost, rules are removed. - dispatchTetherConnectionChanged(null, null); + dispatchTetherConnectionChanged(null, null, 0); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighA, macA)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); reset(mNetd); @@ -715,19 +717,19 @@ public class IpServerTest { // Rules can be added again once upstream IPv6 connectivity is available. lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); // If upstream IPv6 connectivity is lost, rules are removed. reset(mNetd); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, null); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); // When the interface goes down, rules are removed. lp.setInterfaceName(UPSTREAM_IFACE); - dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); @@ -788,6 +790,49 @@ public class IpServerTest { verify(mIpNeighborMonitor, never()).start(); } + private LinkProperties buildIpv6OnlyLinkProperties(final String iface) { + final LinkProperties linkProp = new LinkProperties(); + linkProp.setInterfaceName(iface); + linkProp.addLinkAddress(new LinkAddress("2001:db8::1/64")); + linkProp.addRoute(new RouteInfo(new IpPrefix("::/0"), null, iface, RTN_UNICAST)); + final InetAddress dns = InetAddresses.parseNumericAddress("2001:4860:4860::8888"); + linkProp.addDnsServer(dns); + + return linkProp; + } + + @Test + public void testAdjustTtlValue() throws Exception { + final ArgumentCaptor raParamsCaptor = + ArgumentCaptor.forClass(RaParams.class); + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams noV6Params = raParamsCaptor.getValue(); + assertEquals(65, noV6Params.hopLimit); + reset(mRaDaemon); + + when(mNetd.getProcSysNet( + INetd.IPV6, INetd.CONF, UPSTREAM_IFACE, "hop_limit")).thenReturn("64"); + final LinkProperties lp = buildIpv6OnlyLinkProperties(UPSTREAM_IFACE); + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, 1); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams nonCellularParams = raParamsCaptor.getValue(); + assertEquals(65, nonCellularParams.hopLimit); + reset(mRaDaemon); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams noUpstream = raParamsCaptor.getValue(); + assertEquals(65, nonCellularParams.hopLimit); + reset(mRaDaemon); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1); + verify(mRaDaemon).buildNewRa(any(), raParamsCaptor.capture()); + final RaParams cellularParams = raParamsCaptor.getValue(); + assertEquals(63, cellularParams.hopLimit); + reset(mRaDaemon); + } + private void assertDhcpServingParams(final DhcpServingParamsParcel params, final IpPrefix prefix) { // Last address byte is random @@ -838,9 +883,10 @@ public class IpServerTest { * @param upstreamIface String name of upstream interface (or null) * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream. */ - private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) { + private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp, + int ttlAdjustment) { dispatchTetherConnectionChanged(upstreamIface); - mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp); + mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp); mLooper.dispatchAll(); } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java index 820f255145..f2b5314e5a 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/IPv6TetheringCoordinatorTest.java @@ -128,7 +128,7 @@ public class IPv6TetheringCoordinatorTest { final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); final ArgumentCaptor lp = ArgumentCaptor.forClass(LinkProperties.class); mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); - verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + verify(firstServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), lp.capture()); final LinkProperties v6OnlyLink = lp.getValue(); assertOnlyOneV6AddressAndNoV4(v6OnlyLink); @@ -140,7 +140,7 @@ public class IPv6TetheringCoordinatorTest { mNotifyList.remove(firstServer); mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); - verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(0), eq(0), + verify(secondServer).sendMessage(eq(IpServer.CMD_IPV6_TETHER_UPDATE), eq(-1), eq(0), lp.capture()); final LinkProperties localOnlyLink = lp.getValue(); assertNotNull(localOnlyLink);