Tethering offload: add/remove IPv6 forwarding rules on ND events.

Use IpNeighborMonitor to listen for ND cache events on the
downstream interface, and push downstream IPv6 fowarding rules
to netd.

Rules are pushed when:
- IPv6 neighbours appear/disappear on the downstream interface.
- The upstream changes.

Test: new unit test
Change-Id: I7b01ba179a4d6bb248fd6c4994e48800613a4efa
This commit is contained in:
Lorenzo Colitti
2020-02-14 01:06:35 +09:00
parent d50b3ed6c7
commit 5e15d7b06a
3 changed files with 260 additions and 1 deletions

View File

@@ -40,12 +40,14 @@ import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.DhcpServingParamsParcelExt; import android.net.dhcp.DhcpServingParamsParcelExt;
import android.net.dhcp.IDhcpLeaseCallbacks; import android.net.dhcp.IDhcpLeaseCallbacks;
import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServer;
import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.shared.NetdUtils; import android.net.shared.NetdUtils;
import android.net.shared.RouteUtils; import android.net.shared.RouteUtils;
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;
import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
@@ -59,14 +61,17 @@ import com.android.internal.util.MessageUtils;
import com.android.internal.util.State; import com.android.internal.util.State;
import com.android.internal.util.StateMachine; import com.android.internal.util.StateMachine;
import java.io.IOException;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.Inet6Address; import java.net.Inet6Address;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Random; import java.util.Random;
@@ -149,6 +154,12 @@ public class IpServer extends StateMachine {
/** Capture IpServer dependencies, for injection. */ /** Capture IpServer dependencies, for injection. */
public abstract static class Dependencies { public abstract static class Dependencies {
/** Create an IpNeighborMonitor to be used by this IpServer */
public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
IpNeighborMonitor.NeighborEventConsumer consumer) {
return new IpNeighborMonitor(handler, log, consumer);
}
/** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/ /** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams); return new RouterAdvertisementDaemon(ifParams);
@@ -159,6 +170,15 @@ public class IpServer extends StateMachine {
return InterfaceParams.getByName(ifName); return InterfaceParams.getByName(ifName);
} }
/** Get |ifName|'s interface index. */
public int getIfindex(String ifName) {
try {
return NetworkInterface.getByName(ifName).getIndex();
} catch (IOException | NullPointerException e) {
Log.e(TAG, "Can't determine interface index for interface " + ifName);
return 0;
}
}
/** Create a DhcpServer instance to be used by IpServer. */ /** Create a DhcpServer instance to be used by IpServer. */
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params, public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb); DhcpServerCallbacks cb);
@@ -184,6 +204,8 @@ public class IpServer extends StateMachine {
public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9; public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
// new IPv6 tethering parameters need to be processed // new IPv6 tethering parameters need to be processed
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10; public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
// new neighbor cache entry on our interface
public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
private final State mInitialState; private final State mInitialState;
private final State mLocalHotspotState; private final State mLocalHotspotState;
@@ -223,6 +245,40 @@ public class IpServer extends StateMachine {
@NonNull @NonNull
private List<TetheredClient> mDhcpLeases = Collections.emptyList(); private List<TetheredClient> mDhcpLeases = Collections.emptyList();
private int mLastIPv6UpstreamIfindex = 0;
private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
public void accept(NeighborEvent e) {
sendMessage(CMD_NEIGHBOR_EVENT, e);
}
}
static class Ipv6ForwardingRule {
public final int upstreamIfindex;
public final int downstreamIfindex;
public final Inet6Address address;
public final MacAddress srcMac;
public final MacAddress dstMac;
Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
MacAddress srcMac, MacAddress dstMac) {
this.upstreamIfindex = upstreamIfindex;
this.downstreamIfindex = downstreamIfIndex;
this.address = address;
this.srcMac = srcMac;
this.dstMac = dstMac;
}
public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
dstMac);
}
}
private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
new LinkedHashMap<>();
private final IpNeighborMonitor mIpNeighborMonitor;
public IpServer( public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log, String ifaceName, Looper looper, int interfaceType, SharedLog log,
INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) { INetd netd, Callback callback, boolean usingLegacyDhcp, Dependencies deps) {
@@ -240,6 +296,12 @@ public class IpServer extends StateMachine {
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR; mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE; mServingMode = STATE_AVAILABLE;
mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
new MyNeighborEventConsumer());
if (!mIpNeighborMonitor.start()) {
mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
}
mInitialState = new InitialState(); mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState(); mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState(); mTetheredState = new TetheredState();
@@ -607,13 +669,16 @@ public class IpServer extends StateMachine {
} }
RaParams params = null; RaParams params = null;
int upstreamIfindex = 0;
if (v6only != null) { if (v6only != null) {
final String upstreamIface = v6only.getInterfaceName();
params = new RaParams(); params = new RaParams();
params.mtu = v6only.getMtu(); params.mtu = v6only.getMtu();
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute(); params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName()); if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
for (LinkAddress linkAddr : v6only.getLinkAddresses()) { for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue; if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -627,12 +692,18 @@ public class IpServer extends StateMachine {
params.dnses.add(dnsServer); params.dnses.add(dnsServer);
} }
} }
upstreamIfindex = mDeps.getIfindex(upstreamIface);
} }
// If v6only is null, we pass in null to setRaParams(), which handles // If v6only is null, we pass in null to setRaParams(), which handles
// deprecation of any existing RA data. // deprecation of any existing RA data.
setRaParams(params); setRaParams(params);
mLastIPv6LinkProperties = v6only; mLastIPv6LinkProperties = v6only;
updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
mLastIPv6UpstreamIfindex = upstreamIfindex;
} }
private void configureLocalIPv6Routes( private void configureLocalIPv6Routes(
@@ -727,6 +798,73 @@ public class IpServer extends StateMachine {
} }
} }
private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
try {
mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(),
rule.dstMac.toByteArray());
mIpv6ForwardingRules.put(rule.address, rule);
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Could not add IPv6 downstream rule: " + e);
}
}
private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
try {
mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
if (removeFromMap) {
mIpv6ForwardingRules.remove(rule.address);
}
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Could not remove IPv6 downstream rule: " + e);
}
}
// Convenience method to replace a rule with the same rule on a new upstream interface.
// Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
// Relies on the fact that rules are in a map indexed by IP address.
private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) {
addIpv6ForwardingRule(rule.onNewUpstream(newIfindex));
removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
}
// Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
// changes or if a neighbor event is received.
private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
NeighborEvent e) {
// If the upstream interface has changed, remove all rules and re-add them with the new
// upstream interface.
if (prevUpstreamIfindex != upstreamIfindex) {
for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
updateIpv6ForwardingRule(rule, upstreamIfindex);
}
}
// If we're here to process a NeighborEvent, do so now.
if (e == null) return;
if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
|| e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
return;
}
Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex,
mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
e.macAddr);
if (e.isValid()) {
addIpv6ForwardingRule(rule);
} else {
removeIpv6ForwardingRule(rule, true /*removeFromMap*/);
}
}
private void handleNeighborEvent(NeighborEvent e) {
if (mInterfaceParams != null
&& mInterfaceParams.index == e.ifindex
&& mInterfaceParams.hasMacAddress) {
updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
}
}
private byte getHopLimit(String upstreamIface) { private byte getHopLimit(String upstreamIface) {
try { try {
int upstreamHopLimit = Integer.parseUnsignedInt( int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -1014,6 +1152,9 @@ public class IpServer extends StateMachine {
} }
} }
break; break;
case CMD_NEIGHBOR_EVENT:
handleNeighborEvent((NeighborEvent) message.obj);
break;
default: default:
return false; return false;
} }

View File

@@ -29,6 +29,11 @@ import static android.net.ip.IpServer.STATE_AVAILABLE;
import static android.net.ip.IpServer.STATE_LOCAL_ONLY; import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED; import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE; import static android.net.ip.IpServer.STATE_UNAVAILABLE;
import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
import static android.net.netlink.StructNdMsg.NUD_FAILED;
import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
import static android.net.netlink.StructNdMsg.NUD_STALE;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH; import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -41,6 +46,7 @@ import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq; import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
@@ -52,6 +58,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import android.net.INetd; import android.net.INetd;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel; import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix; import android.net.IpPrefix;
import android.net.LinkAddress; import android.net.LinkAddress;
@@ -61,6 +68,8 @@ import android.net.RouteInfo;
import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer; 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.NeighborEventConsumer;
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;
@@ -81,6 +90,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import java.net.Inet4Address; import java.net.Inet4Address;
import java.net.InetAddress;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
@SmallTest @SmallTest
@@ -88,6 +98,8 @@ public class IpServerTest {
private static final String IFACE_NAME = "testnet1"; private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0"; private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1"; private static final String UPSTREAM_IFACE2 = "upstream1";
private static final int UPSTREAM_IFINDEX = 101;
private static final int UPSTREAM_IFINDEX2 = 102;
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1"; private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24; private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600; private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -102,6 +114,7 @@ public class IpServerTest {
@Mock private SharedLog mSharedLog; @Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer; @Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon; @Mock private RouterAdvertisementDaemon mRaDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies; @Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor; @Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -111,6 +124,7 @@ public class IpServerTest {
ArgumentCaptor.forClass(LinkProperties.class); ArgumentCaptor.forClass(LinkProperties.class);
private IpServer mIpServer; private IpServer mIpServer;
private InterfaceConfigurationParcel mInterfaceConfiguration; private InterfaceConfigurationParcel mInterfaceConfiguration;
private NeighborEventConsumer mNeighborEventConsumer;
private void initStateMachine(int interfaceType) throws Exception { private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */); initStateMachine(interfaceType, false /* usingLegacyDhcp */);
@@ -130,16 +144,28 @@ public class IpServerTest {
}).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any()); }).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon); when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS); when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
mInterfaceConfiguration = new InterfaceConfigurationParcel(); mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0]; mInterfaceConfiguration.flags = new String[0];
if (interfaceType == TETHERING_BLUETOOTH) { if (interfaceType == TETHERING_BLUETOOTH) {
mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR; mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH; mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
} }
ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
ArgumentCaptor.forClass(NeighborEventConsumer.class);
doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
neighborCaptor.capture());
mIpServer = new IpServer( mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd, IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies); mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start(); mIpServer.start();
mNeighborEventConsumer = neighborCaptor.getValue();
// Starting the state machine always puts us in a consistent state and notifies // Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state. // the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll(); mLooper.dispatchAll();
@@ -172,6 +198,8 @@ public class IpServerTest {
@Test @Test
public void startsOutAvailable() { public void startsOutAvailable() {
when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
.thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog, mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies); mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
mIpServer.start(); mIpServer.start();
@@ -469,6 +497,89 @@ public class IpServerTest {
verify(mDependencies, never()).makeDhcpServer(any(), any(), any()); verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
} }
private InetAddress addr(String addr) throws Exception {
return InetAddresses.parseNumericAddress(addr);
}
private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
nudState, mac));
mLooper.dispatchAll();
}
private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
nudState, mac));
mLooper.dispatchAll();
}
@Test
public void addRemoveipv6ForwardingRules() throws Exception {
initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
final int myIfindex = TEST_IFACE_PARAMS.index;
final int notMyIfindex = myIfindex - 1;
final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
reset(mNetd);
// Events on other interfaces are ignored.
recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
verifyNoMoreInteractions(mNetd);
// Events on this interface are received and sent to netd.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
reset(mNetd);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
reset(mNetd);
// Link-local and multicast neighbors are ignored.
recvNewNeigh(notMyIfindex, neighLL, NUD_REACHABLE, macA);
verifyNoMoreInteractions(mNetd);
recvNewNeigh(notMyIfindex, neighMC, NUD_REACHABLE, macA);
verifyNoMoreInteractions(mNetd);
// A neighbor that is no longer valid causes the rule to be removed.
recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
reset(mNetd);
// A neighbor that is deleted causes the rule to be removed.
recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
reset(mNetd);
// Upstream changes result in deleting and re-adding the rules.
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
reset(mNetd);
InOrder inOrder = inOrder(mNetd);
LinkProperties lp = new LinkProperties();
lp.setInterfaceName(UPSTREAM_IFACE2);
dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
eq(neighA.getAddress()));
inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress()));
}
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any()); verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks( verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(

View File

@@ -95,6 +95,7 @@ import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks; import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel; import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer; import android.net.dhcp.IDhcpServer;
import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer; import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams; import android.net.util.InterfaceParams;
@@ -173,6 +174,7 @@ public class TetheringTest {
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
@Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IDhcpServer mDhcpServer; @Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd; @Mock private INetd mNetd;
@Mock private UserManager mUserManager; @Mock private UserManager mUserManager;
@@ -278,6 +280,11 @@ public class TetheringTest {
} }
}).run(); }).run();
} }
public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l,
IpNeighborMonitor.NeighborEventConsumer c) {
return mIpNeighborMonitor;
}
} }
private class MockTetheringConfiguration extends TetheringConfiguration { private class MockTetheringConfiguration extends TetheringConfiguration {