Clear IPv6 forwarding rules when losing upstream or stopping.

Test: new unit test
Change-Id: I8626932e43e0daa300dad5fe6a81f47a6d667030
This commit is contained in:
Lorenzo Colitti
2020-02-21 20:21:14 +09:00
parent 03a734f352
commit c61fc087b2
2 changed files with 65 additions and 3 deletions

View File

@@ -805,7 +805,7 @@ public class IpServer extends StateMachine {
rule.dstMac.toByteArray()); rule.dstMac.toByteArray());
mIpv6ForwardingRules.put(rule.address, rule); mIpv6ForwardingRules.put(rule.address, rule);
} catch (RemoteException | ServiceSpecificException e) { } catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Could not add IPv6 downstream rule: " + e); mLog.e("Could not add IPv6 downstream rule: ", e);
} }
} }
@@ -816,10 +816,17 @@ public class IpServer extends StateMachine {
mIpv6ForwardingRules.remove(rule.address); mIpv6ForwardingRules.remove(rule.address);
} }
} catch (RemoteException | ServiceSpecificException e) { } catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Could not remove IPv6 downstream rule: " + e); mLog.e("Could not remove IPv6 downstream rule: ", e);
} }
} }
private void clearIpv6ForwardingRules() {
for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
}
mIpv6ForwardingRules.clear();
}
// Convenience method to replace a rule with the same rule on a new upstream interface. // 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. // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
// Relies on the fact that rules are in a map indexed by IP address. // Relies on the fact that rules are in a map indexed by IP address.
@@ -832,6 +839,12 @@ public class IpServer extends StateMachine {
// changes or if a neighbor event is received. // changes or if a neighbor event is received.
private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex, private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
NeighborEvent e) { NeighborEvent e) {
// If we no longer have an upstream, clear forwarding rules and do nothing else.
if (upstreamIfindex == 0) {
clearIpv6ForwardingRules();
return;
}
// If the upstream interface has changed, remove all rules and re-add them with the new // If the upstream interface has changed, remove all rules and re-add them with the new
// upstream interface. // upstream interface.
if (prevUpstreamIfindex != upstreamIfindex) { if (prevUpstreamIfindex != upstreamIfindex) {
@@ -841,13 +854,14 @@ public class IpServer extends StateMachine {
} }
// If we're here to process a NeighborEvent, do so now. // If we're here to process a NeighborEvent, do so now.
// mInterfaceParams must be non-null or the event would not have arrived.
if (e == null) return; if (e == null) return;
if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress() if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
|| e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) { || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
return; return;
} }
Ipv6ForwardingRule rule = new Ipv6ForwardingRule(mLastIPv6UpstreamIfindex, Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr, mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
e.macAddr); e.macAddr);
if (e.isValid()) { if (e.isValid()) {
@@ -1090,6 +1104,7 @@ public class IpServer extends StateMachine {
for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
mUpstreamIfaceSet = null; mUpstreamIfaceSet = null;
clearIpv6ForwardingRules();
} }
private void cleanupUpstreamInterface(String upstreamIface) { private void cleanupUpstreamInterface(String upstreamIface) {

View File

@@ -43,6 +43,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
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;
@@ -578,6 +579,52 @@ public class IpServerTest {
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray())); eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress())); eq(neighB.getAddress()));
reset(mNetd);
// When the upstream is lost, rules are removed.
dispatchTetherConnectionChanged(null, null);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
eq(neighA.getAddress()));
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
eq(neighB.getAddress()));
reset(mNetd);
// If the upstream is IPv4-only, no rules are added.
dispatchTetherConnectionChanged(UPSTREAM_IFACE);
reset(mNetd);
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
verifyNoMoreInteractions(mNetd);
// Rules can be added again once upstream IPv6 connectivity is available.
lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(),
eq(neighA.getAddress()), any(), any());
// If upstream IPv6 connectivity is lost, rules are removed.
reset(mNetd);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
// When the interface goes down, rules are removed.
lp.setInterfaceName(UPSTREAM_IFACE);
dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
reset(mNetd);
mIpServer.stop();
mLooper.dispatchAll();
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
reset(mNetd);
} }
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception { private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {