Adjust TTL for ipv6 tethering
If upstream is cellular, set the TTL in Router Advertisements to
"network-set TTL - 1" for carrier requirement. For other non-cellular
upstream, set TTL as "network-set TTL + 1" to preventing arbitrary
distinction between tethered and untethered traffic.
Bug: 154776299
Test: atest TetheringTests
Merged-In: I7f2696a642f96c6aafb5613b980bf5bcdd08bbda
Change-Id: I7f2696a642f96c6aafb5613b980bf5bcdd08bbda
diff --git a/Tethering/src/android/net/ip/IpServer.java b/Tethering/src/android/net/ip/IpServer.java
index de53787..659d344 100644
--- a/Tethering/src/android/net/ip/IpServer.java
+++ b/Tethering/src/android/net/ip/IpServer.java
@@ -738,7 +738,7 @@
//
// 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 @@
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 @@
}
}
- 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 @@
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 @@
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 d450c46..f3dcaa2 100644
--- a/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/IPv6TetheringCoordinator.java
@@ -161,11 +161,28 @@
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 cd1ff60..307ebf1 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.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 @@
if (upstreamIface != null) {
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(upstreamIface);
- dispatchTetherConnectionChanged(upstreamIface, lp);
+ dispatchTetherConnectionChanged(upstreamIface, lp, 0);
}
reset(mNetd, mCallback);
}
@@ -694,7 +696,7 @@
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 @@
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 @@
// 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 @@
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<RaParams> 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 @@
* @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 820f255..f2b5314 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 @@
final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
final ArgumentCaptor<LinkProperties> 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 @@
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);