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

Change-Id: I7f2696a642f96c6aafb5613b980bf5bcdd08bbda
This commit is contained in:
markchien
2020-05-21 17:38:28 +08:00
parent 366eae572d
commit d63c4f35de
4 changed files with 80 additions and 18 deletions

View File

@@ -738,7 +738,7 @@ public class IpServer extends StateMachine {
// //
// TODO: Evaluate using a data structure than is more directly suited to // TODO: Evaluate using a data structure than is more directly suited to
// communicating only the relevant information. // communicating only the relevant information.
private void updateUpstreamIPv6LinkProperties(LinkProperties v6only) { private void updateUpstreamIPv6LinkProperties(LinkProperties v6only, int ttlAdjustment) {
if (mRaDaemon == null) return; if (mRaDaemon == null) return;
// Avoid unnecessary work on spurious updates. // Avoid unnecessary work on spurious updates.
@@ -761,7 +761,7 @@ public class IpServer extends StateMachine {
params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu(); params.mtu = mUsingBpfOffload ? v6only.getMtu() - 16 : v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface, ttlAdjustment);
for (LinkAddress linkAddr : v6only.getLinkAddresses()) { for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; 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 { try {
int upstreamHopLimit = Integer.parseUnsignedInt( int upstreamHopLimit = Integer.parseUnsignedInt(
mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit")); mNetd.getProcSysNet(INetd.IPV6, INetd.CONF, upstreamIface, "hop_limit"));
// Add one hop to account for this forwarding device upstreamHopLimit = upstreamHopLimit + adjustTTL;
upstreamHopLimit++;
// Cap the hop limit to 255. // Cap the hop limit to 255.
return (byte) Integer.min(upstreamHopLimit, 255); return (byte) Integer.min(upstreamHopLimit, 255);
} catch (Exception e) { } catch (Exception e) {
@@ -1145,7 +1144,7 @@ public class IpServer extends StateMachine {
transitionTo(mUnavailableState); transitionTo(mUnavailableState);
break; break;
case CMD_IPV6_TETHER_UPDATE: case CMD_IPV6_TETHER_UPDATE:
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
break; break;
default: default:
return NOT_HANDLED; return NOT_HANDLED;
@@ -1209,7 +1208,7 @@ public class IpServer extends StateMachine {
if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName); if (DBG) Log.d(TAG, "Untethered (ifdown)" + mIfaceName);
break; break;
case CMD_IPV6_TETHER_UPDATE: case CMD_IPV6_TETHER_UPDATE:
updateUpstreamIPv6LinkProperties((LinkProperties) message.obj); updateUpstreamIPv6LinkProperties((LinkProperties) message.obj, message.arg1);
sendLinkProperties(); sendLinkProperties();
break; break;
case CMD_IP_FORWARDING_ENABLE_ERROR: case CMD_IP_FORWARDING_ENABLE_ERROR:

View File

@@ -161,11 +161,28 @@ public class IPv6TetheringCoordinator {
private void updateIPv6TetheringInterfaces() { private void updateIPv6TetheringInterfaces() {
for (IpServer ipServer : mNotifyList) { for (IpServer ipServer : mNotifyList) {
final LinkProperties lp = getInterfaceIPv6LinkProperties(ipServer); 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; 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) { private LinkProperties getInterfaceIPv6LinkProperties(IpServer ipServer) {
final Downstream ds = findDownstream(ipServer); final Downstream ds = findDownstream(ipServer);
if (ds == null) return null; if (ds == null) return null;

View File

@@ -17,6 +17,7 @@
package android.net.ip; package android.net.ip;
import static android.net.INetd.IF_STATE_UP; 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_BLUETOOTH;
import static android.net.TetheringManager.TETHERING_NCM; import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB; import static android.net.TetheringManager.TETHERING_USB;
@@ -74,6 +75,7 @@ import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks; import android.net.dhcp.IDhcpServerCallbacks;
import android.net.ip.IpNeighborMonitor.NeighborEvent; import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.IpNeighborMonitor.NeighborEventConsumer; import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.InterfaceParams; import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet; import android.net.util.InterfaceSet;
import android.net.util.SharedLog; import android.net.util.SharedLog;
@@ -196,7 +198,7 @@ public class IpServerTest {
if (upstreamIface != null) { if (upstreamIface != null) {
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
lp.setInterfaceName(upstreamIface); lp.setInterfaceName(upstreamIface);
dispatchTetherConnectionChanged(upstreamIface, lp); dispatchTetherConnectionChanged(upstreamIface, lp, 0);
} }
reset(mNetd, mCallback); reset(mNetd, mCallback);
} }
@@ -694,7 +696,7 @@ public class IpServerTest {
InOrder inOrder = inOrder(mNetd); InOrder inOrder = inOrder(mNetd);
LinkProperties lp = new LinkProperties(); LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2); 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).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA)); inOrder.verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighA, macA));
inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB)); inOrder.verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX2, neighB, macB));
@@ -702,7 +704,7 @@ public class IpServerTest {
reset(mNetd); reset(mNetd);
// When the upstream is lost, rules are removed. // 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, neighA, macA));
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX2, neighB, macB));
reset(mNetd); reset(mNetd);
@@ -715,19 +717,19 @@ public class IpServerTest {
// Rules can be added again once upstream IPv6 connectivity is available. // Rules can be added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE); lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighB, macB));
verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd, never()).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
// If upstream IPv6 connectivity is lost, rules are removed. // If upstream IPv6 connectivity is lost, rules are removed.
reset(mNetd); reset(mNetd);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null); dispatchTetherConnectionChanged(UPSTREAM_IFACE, null, 0);
verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB)); verify(mNetd).tetherOffloadRuleRemove(matches(UPSTREAM_IFINDEX, neighB, macB));
// When the interface goes down, rules are removed. // When the interface goes down, rules are removed.
lp.setInterfaceName(UPSTREAM_IFACE); lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp); dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp, -1);
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA); recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB); recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA)); verify(mNetd).tetherOffloadRuleAdd(matches(UPSTREAM_IFINDEX, neighA, macA));
@@ -788,6 +790,49 @@ public class IpServerTest {
verify(mIpNeighborMonitor, never()).start(); 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, private void assertDhcpServingParams(final DhcpServingParamsParcel params,
final IpPrefix prefix) { final IpPrefix prefix) {
// Last address byte is random // Last address byte is random
@@ -838,9 +883,10 @@ public class IpServerTest {
* @param upstreamIface String name of upstream interface (or null) * @param upstreamIface String name of upstream interface (or null)
* @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream. * @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); dispatchTetherConnectionChanged(upstreamIface);
mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp); mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, ttlAdjustment, 0, v6lp);
mLooper.dispatchAll(); mLooper.dispatchAll();
} }

View File

@@ -128,7 +128,7 @@ public class IPv6TetheringCoordinatorTest {
final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR); final UpstreamNetworkState mobileUpstream = createDualStackUpstream(TRANSPORT_CELLULAR);
final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class); final ArgumentCaptor<LinkProperties> lp = ArgumentCaptor.forClass(LinkProperties.class);
mIPv6TetheringCoordinator.updateUpstreamNetworkState(mobileUpstream); 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()); lp.capture());
final LinkProperties v6OnlyLink = lp.getValue(); final LinkProperties v6OnlyLink = lp.getValue();
assertOnlyOneV6AddressAndNoV4(v6OnlyLink); assertOnlyOneV6AddressAndNoV4(v6OnlyLink);
@@ -140,7 +140,7 @@ public class IPv6TetheringCoordinatorTest {
mNotifyList.remove(firstServer); mNotifyList.remove(firstServer);
mIPv6TetheringCoordinator.removeActiveDownstream(firstServer); mIPv6TetheringCoordinator.removeActiveDownstream(firstServer);
verify(firstServer).sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, 0, 0, null); 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()); lp.capture());
final LinkProperties localOnlyLink = lp.getValue(); final LinkProperties localOnlyLink = lp.getValue();
assertNotNull(localOnlyLink); assertNotNull(localOnlyLink);