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:
@@ -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:
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user