summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXiao Ma <xiaom@google.com>2020-05-25 09:08:46 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2020-05-25 09:08:46 +0000
commitb664b89668e26eff904a5d52eed7e98b2355f2e6 (patch)
tree0dcbc2e1cdec1ac99637f588508ec22c780de6f7
parent4376550b5b3247327012aa28e60b53254aed255f (diff)
parente86b4150de8265ff6e8b1c5f977565ad0309e5c3 (diff)
Merge "Support MirrorLink DHCPDECLINE." into rvc-dev
-rw-r--r--src/android/net/dhcp/DhcpLeaseRepository.java22
-rw-r--r--src/android/net/dhcp/DhcpServer.java241
-rw-r--r--tests/unit/src/android/net/dhcp/DhcpServerTest.java144
3 files changed, 299 insertions, 108 deletions
diff --git a/src/android/net/dhcp/DhcpLeaseRepository.java b/src/android/net/dhcp/DhcpLeaseRepository.java
index 3639a2d..b7a2572 100644
--- a/src/android/net/dhcp/DhcpLeaseRepository.java
+++ b/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -37,6 +37,7 @@ import android.util.ArrayMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import java.net.Inet4Address;
import java.util.ArrayList;
@@ -158,7 +159,7 @@ class DhcpLeaseRepository {
/**
* From a map keyed by {@link Inet4Address}, remove entries where the key is invalid (as
* specified by {@link #isValidAddress(Inet4Address)}), or is a reserved address.
- * @return true iff at least one entry was removed.
+ * @return true if and only if at least one entry was removed.
*/
private <T> boolean cleanMap(Map<Inet4Address, T> map) {
final Iterator<Entry<Inet4Address, T>> it = map.entrySet().iterator();
@@ -397,7 +398,8 @@ class DhcpLeaseRepository {
mEventCallbacks.finishBroadcast();
}
- public void markLeaseDeclined(@NonNull Inet4Address addr) {
+ @VisibleForTesting
+ void markLeaseDeclined(@NonNull Inet4Address addr) {
if (mDeclinedAddrs.containsKey(addr) || !isValidAddress(addr)) {
mLog.logf("Not marking %s as declined: already declined or not assignable",
inet4AddrToString(addr));
@@ -410,6 +412,22 @@ class DhcpLeaseRepository {
}
/**
+ * Mark a committed lease matching the passed in clientId and hardware address parameters to be
+ * declined, and delete it from the repository.
+ *
+ * @param clientId Client identifier option if specified, or {@link #CLIENTID_UNSPEC}
+ * @param hwAddr client's mac address
+ * @param Addr IPv4 address to be declined
+ * @return true if a lease matching parameters was removed from committed repository.
+ */
+ public boolean markAndReleaseDeclinedLease(@Nullable byte[] clientId,
+ @NonNull MacAddress hwAddr, @NonNull Inet4Address addr) {
+ if (!releaseLease(clientId, hwAddr, addr)) return false;
+ markLeaseDeclined(addr);
+ return true;
+ }
+
+ /**
* Get the list of currently valid committed leases in the repository.
*/
@NonNull
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 212e609..55b1f28 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -45,6 +45,7 @@ import static java.lang.Integer.toUnsignedLong;
import android.content.Context;
import android.net.INetworkStackStatusCallback;
+import android.net.IpPrefix;
import android.net.MacAddress;
import android.net.TrafficStats;
import android.net.util.NetworkStackUtils;
@@ -96,7 +97,8 @@ public class DhcpServer extends StateMachine {
private static final int CMD_START_DHCP_SERVER = 1;
private static final int CMD_STOP_DHCP_SERVER = 2;
private static final int CMD_UPDATE_PARAMS = 3;
- private static final int CMD_SUSPEND_DHCP_SERVER = 4;
+ @VisibleForTesting
+ protected static final int CMD_RECEIVE_PACKET = 4;
@NonNull
private final Context mContext;
@@ -126,6 +128,8 @@ public class DhcpServer extends StateMachine {
private final StoppedState mStoppedState = new StoppedState();
private final StartedState mStartedState = new StartedState();
private final RunningState mRunningState = new RunningState();
+ private final WaitBeforeRetrievalState mWaitBeforeRetrievalState =
+ new WaitBeforeRetrievalState();
/**
* Clock to be used by DhcpServer to track time for lease expiration.
@@ -262,6 +266,7 @@ public class DhcpServer extends StateMachine {
addState(mStoppedState);
addState(mStartedState);
addState(mRunningState, mStartedState);
+ addState(mWaitBeforeRetrievalState, mStartedState);
// CHECKSTYLE:ON IndentationCheck
setInitialState(mStoppedState);
@@ -372,6 +377,17 @@ public class DhcpServer extends StateMachine {
}
}
+ private void handleUpdateServingParams(@NonNull DhcpServingParams params,
+ @Nullable INetworkStackStatusCallback cb) {
+ mServingParams = params;
+ mLeaseRepo.updateParams(
+ DhcpServingParams.makeIpPrefix(params.serverAddr),
+ params.excludedAddrs,
+ params.dhcpLeaseTimeSecs * 1000,
+ params.singleClientAddr);
+ maybeNotifyStatus(cb, STATUS_SUCCESS);
+ }
+
class StoppedState extends State {
private INetworkStackStatusCallback mOnStopCallback;
@@ -422,6 +438,8 @@ public class DhcpServer extends StateMachine {
mLeaseRepo.addLeaseCallbacks(mEventCallbacks);
}
maybeNotifyStatus(mOnStartCallback, STATUS_SUCCESS);
+ // Clear INetworkStackStatusCallback binder token, so that it's freed
+ // on the other side.
mOnStartCallback = null;
}
@@ -431,14 +449,7 @@ public class DhcpServer extends StateMachine {
case CMD_UPDATE_PARAMS:
final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
(Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
- final DhcpServingParams params = pair.first;
- mServingParams = params;
- mLeaseRepo.updateParams(
- DhcpServingParams.makeIpPrefix(params.serverAddr),
- params.excludedAddrs,
- params.dhcpLeaseTimeSecs * 1000,
- params.singleClientAddr);
- maybeNotifyStatus(pair.second, STATUS_SUCCESS);
+ handleUpdateServingParams(pair.first, pair.second);
return HANDLED;
case CMD_START_DHCP_SERVER:
@@ -466,100 +477,158 @@ public class DhcpServer extends StateMachine {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
- case CMD_SUSPEND_DHCP_SERVER:
- // TODO: transition to the state which waits for IpServer to reconfigure the
- // new selected prefix.
+ case CMD_RECEIVE_PACKET:
+ processPacket((DhcpPacket) msg.obj);
return HANDLED;
+
default:
// Fall through to StartedState.
return NOT_HANDLED;
}
}
- }
- @VisibleForTesting
- void processPacket(@NonNull DhcpPacket packet, int srcPort) {
- final String packetType = packet.getClass().getSimpleName();
- if (srcPort != DHCP_CLIENT) {
- mLog.logf("Ignored packet of type %s sent from client port %d", packetType, srcPort);
- return;
- }
+ private void processPacket(@NonNull DhcpPacket packet) {
+ mLog.log("Received packet of type " + packet.getClass().getSimpleName());
- mLog.log("Received packet of type " + packetType);
- final Inet4Address sid = packet.mServerIdentifier;
- if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
- mLog.log("Packet ignored due to wrong server identifier: " + sid);
- return;
- }
+ final Inet4Address sid = packet.mServerIdentifier;
+ if (sid != null && !sid.equals(mServingParams.serverAddr.getAddress())) {
+ mLog.log("Packet ignored due to wrong server identifier: " + sid);
+ return;
+ }
- try {
- if (packet instanceof DhcpDiscoverPacket) {
- processDiscover((DhcpDiscoverPacket) packet);
- } else if (packet instanceof DhcpRequestPacket) {
- processRequest((DhcpRequestPacket) packet);
- } else if (packet instanceof DhcpReleasePacket) {
- processRelease((DhcpReleasePacket) packet);
- } else {
- mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
+ try {
+ if (packet instanceof DhcpDiscoverPacket) {
+ processDiscover((DhcpDiscoverPacket) packet);
+ } else if (packet instanceof DhcpRequestPacket) {
+ processRequest((DhcpRequestPacket) packet);
+ } else if (packet instanceof DhcpReleasePacket) {
+ processRelease((DhcpReleasePacket) packet);
+ } else if (packet instanceof DhcpDeclinePacket) {
+ processDecline((DhcpDeclinePacket) packet);
+ } else {
+ mLog.e("Unknown packet type: " + packet.getClass().getSimpleName());
+ }
+ } catch (MalformedPacketException e) {
+ // Not an internal error: only logging exception message, not stacktrace
+ mLog.e("Ignored malformed packet: " + e.getMessage());
}
- } catch (MalformedPacketException e) {
+ }
+
+ private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
// Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored malformed packet: " + e.getMessage());
+ mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
}
- }
- private void logIgnoredPacketInvalidSubnet(DhcpLeaseRepository.InvalidSubnetException e) {
- // Not an internal error: only logging exception message, not stacktrace
- mLog.e("Ignored packet from invalid subnet: " + e.getMessage());
- }
+ private void processDiscover(@NonNull DhcpDiscoverPacket packet)
+ throws MalformedPacketException {
+ final DhcpLease lease;
+ final MacAddress clientMac = getMacAddr(packet);
+ try {
+ if (mDhcpRapidCommitEnabled && packet.mRapidCommit) {
+ lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(),
+ clientMac, packet.mRelayIp, packet.mHostName);
+ transmitAck(packet, lease, clientMac);
+ } else {
+ lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
+ packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
+ transmitOffer(packet, lease, clientMac);
+ }
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) {
+ transmitNak(packet, "Out of addresses to offer");
+ } catch (DhcpLeaseRepository.InvalidSubnetException e) {
+ logIgnoredPacketInvalidSubnet(e);
+ }
+ }
- private void processDiscover(@NonNull DhcpDiscoverPacket packet)
- throws MalformedPacketException {
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- if (mDhcpRapidCommitEnabled && packet.mRapidCommit) {
- lease = mLeaseRepo.getCommittedLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mHostName);
- transmitAck(packet, lease, clientMac);
- } else {
- lease = mLeaseRepo.getOffer(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mRelayIp, packet.mRequestedIp, packet.mHostName);
- transmitOffer(packet, lease, clientMac);
+ private void processRequest(@NonNull DhcpRequestPacket packet)
+ throws MalformedPacketException {
+ // If set, packet SID matches with this server's ID as checked in processPacket().
+ final boolean sidSet = packet.mServerIdentifier != null;
+ final DhcpLease lease;
+ final MacAddress clientMac = getMacAddr(packet);
+ try {
+ lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
+ packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
+ packet.mHostName);
+ } catch (DhcpLeaseRepository.InvalidAddressException e) {
+ transmitNak(packet, "Invalid requested address");
+ return;
+ } catch (DhcpLeaseRepository.InvalidSubnetException e) {
+ logIgnoredPacketInvalidSubnet(e);
+ return;
}
- } catch (DhcpLeaseRepository.OutOfAddressesException e) {
- transmitNak(packet, "Out of addresses to offer");
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
+
+ transmitAck(packet, lease, clientMac);
}
- }
- private void processRequest(@NonNull DhcpRequestPacket packet) throws MalformedPacketException {
- // If set, packet SID matches with this server's ID as checked in processPacket().
- final boolean sidSet = packet.mServerIdentifier != null;
- final DhcpLease lease;
- final MacAddress clientMac = getMacAddr(packet);
- try {
- lease = mLeaseRepo.requestLease(packet.getExplicitClientIdOrNull(), clientMac,
- packet.mClientIp, packet.mRelayIp, packet.mRequestedIp, sidSet,
- packet.mHostName);
- } catch (DhcpLeaseRepository.InvalidAddressException e) {
- transmitNak(packet, "Invalid requested address");
- return;
- } catch (DhcpLeaseRepository.InvalidSubnetException e) {
- logIgnoredPacketInvalidSubnet(e);
- return;
+ private void processRelease(@NonNull DhcpReleasePacket packet)
+ throws MalformedPacketException {
+ final byte[] clientId = packet.getExplicitClientIdOrNull();
+ final MacAddress macAddr = getMacAddr(packet);
+ // Don't care about success (there is no ACK/NAK); logging is already done
+ // in the repository.
+ mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
}
- transmitAck(packet, lease, clientMac);
+ private void processDecline(@NonNull DhcpDeclinePacket packet)
+ throws MalformedPacketException {
+ final byte[] clientId = packet.getExplicitClientIdOrNull();
+ final MacAddress macAddr = getMacAddr(packet);
+ int committedLeasesCount = mLeaseRepo.getCommittedLeases().size();
+
+ // If peer's clientID and macAddr doesn't match with any issued lease, nothing to do.
+ if (!mLeaseRepo.markAndReleaseDeclinedLease(clientId, macAddr, packet.mRequestedIp)) {
+ return;
+ }
+
+ // Check whether the boolean flag which requests a new prefix is enabled, and if
+ // it's enabled, make sure the issued lease count should be only one, otherwise,
+ // changing a different prefix will cause other exist host(s) configured with the
+ // current prefix lose appropriate route.
+ if (!mServingParams.changePrefixOnDecline || committedLeasesCount > 1) return;
+
+ if (mEventCallbacks == null) {
+ mLog.e("changePrefixOnDecline enabled but caller didn't pass a valid"
+ + "IDhcpEventCallbacks callback.");
+ return;
+ }
+
+ try {
+ mEventCallbacks.onNewPrefixRequest(
+ DhcpServingParams.makeIpPrefix(mServingParams.serverAddr));
+ transitionTo(mWaitBeforeRetrievalState);
+ } catch (RemoteException e) {
+ mLog.e("could not request a new prefix to caller", e);
+ }
+ }
}
- private void processRelease(@NonNull DhcpReleasePacket packet)
- throws MalformedPacketException {
- final byte[] clientId = packet.getExplicitClientIdOrNull();
- final MacAddress macAddr = getMacAddr(packet);
- // Don't care about success (there is no ACK/NAK); logging is already done in the repository
- mLeaseRepo.releaseLease(clientId, macAddr, packet.mClientIp);
+ class WaitBeforeRetrievalState extends State {
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_UPDATE_PARAMS:
+ final Pair<DhcpServingParams, INetworkStackStatusCallback> pair =
+ (Pair<DhcpServingParams, INetworkStackStatusCallback>) msg.obj;
+ final IpPrefix currentPrefix =
+ DhcpServingParams.makeIpPrefix(mServingParams.serverAddr);
+ final IpPrefix newPrefix =
+ DhcpServingParams.makeIpPrefix(pair.first.serverAddr);
+ handleUpdateServingParams(pair.first, pair.second);
+ if (currentPrefix != null && !currentPrefix.equals(newPrefix)) {
+ transitionTo(mRunningState);
+ }
+ return HANDLED;
+
+ case CMD_RECEIVE_PACKET:
+ deferMessage(msg);
+ return HANDLED;
+
+ default:
+ // Fall through to StartedState.
+ return NOT_HANDLED;
+ }
+ }
}
private Inet4Address getAckOrOfferDst(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
@@ -748,7 +817,13 @@ public class DhcpServer extends StateMachine {
@Override
protected void onReceive(@NonNull DhcpPacket packet, @NonNull Inet4Address srcAddr,
int srcPort) {
- processPacket(packet, srcPort);
+ if (srcPort != DHCP_CLIENT) {
+ final String packetType = packet.getClass().getSimpleName();
+ mLog.logf("Ignored packet of type %s sent from client port %d",
+ packetType, srcPort);
+ return;
+ }
+ sendMessage(CMD_RECEIVE_PACKET, packet);
}
@Override
diff --git a/tests/unit/src/android/net/dhcp/DhcpServerTest.java b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
index c925789..2e8a3c2 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServerTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServerTest.java
@@ -17,12 +17,13 @@
package android.net.dhcp;
import static android.net.InetAddresses.parseNumericAddress;
-import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
import static android.net.dhcp.DhcpPacket.INADDR_ANY;
import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.net.dhcp.DhcpServer.CMD_RECEIVE_PACKET;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.inet4AddressToIntHTH;
import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
import static junit.framework.Assert.assertEquals;
@@ -31,7 +32,6 @@ import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
@@ -44,12 +44,14 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.INetworkStackStatusCallback;
+import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.MacAddress;
import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
import android.net.dhcp.DhcpServer.Clock;
import android.net.dhcp.DhcpServer.Dependencies;
+import android.net.shared.Inet4AddressUtils;
import android.net.util.SharedLog;
import android.testing.AndroidTestingRunner;
@@ -69,6 +71,8 @@ import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
@@ -156,6 +160,7 @@ public class DhcpServerTest {
.setServerAddr(TEST_SERVER_LINKADDR)
.setLinkMtu(TEST_MTU)
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
+ .setChangePrefixOnDecline(false)
.build();
}
@@ -215,7 +220,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpOfferPacket packet = assertOffer(getPacket());
@@ -233,7 +239,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, true /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -251,7 +258,8 @@ public class DhcpServerTest {
final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
(short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
- mServer.processPacket(discover, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -279,7 +287,8 @@ public class DhcpServerTest {
final DhcpRequestPacket request = makeRequestSelectingPacket();
request.mHostName = TEST_HOSTNAME;
request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
- mServer.processPacket(request, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, request);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(TEST_CLIENT_ADDR);
final DhcpAckPacket packet = assertAck(getPacket());
@@ -296,7 +305,8 @@ public class DhcpServerTest {
.thenThrow(new InvalidAddressException("Test error"));
final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, request);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
assertResponseSentTo(INADDR_BROADCAST);
final DhcpNakPacket packet = assertNak(getPacket());
@@ -304,46 +314,134 @@ public class DhcpServerTest {
}
@Test
- public void testRequest_Selecting_WrongClientPort() throws Exception {
- startServer();
-
- final DhcpRequestPacket request = makeRequestSelectingPacket();
- mServer.processPacket(request, 50000);
-
- verify(mRepository, never())
- .requestLease(any(), any(), any(), any(), any(), anyBoolean(), any());
- verify(mDeps, never()).sendPacket(any(), any(), any());
- }
-
- @Test
public void testRelease() throws Exception {
startServer();
final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID,
TEST_SERVER_ADDR, TEST_CLIENT_ADDR,
INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES);
- mServer.processPacket(release, DHCP_CLIENT);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, release);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
verify(mRepository, times(1))
.releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR));
}
+ @Test
+ public void testDecline_LeaseDoesNotExist() throws Exception {
+ when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR))).thenReturn(false);
+
+ startServer();
+ runOnReceivedDeclinePacket();
+ verify(mEventCallbacks, never()).onNewPrefixRequest(any());
+ }
+
+ private void runOnReceivedDeclinePacket() throws Exception {
+ when(mRepository.getCommittedLeases()).thenReturn(
+ Arrays.asList(new DhcpLease(null, TEST_CLIENT_MAC, TEST_CLIENT_ADDR,
+ TEST_PREFIX_LENGTH, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ TEST_HOSTNAME)));
+ final DhcpDeclinePacket decline = new DhcpDeclinePacket(TEST_TRANSACTION_ID,
+ (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
+ INADDR_ANY /* nextIp */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
+ TEST_CLIENT_ADDR /* requestedIp */, TEST_SERVER_ADDR /* serverIdentifier */);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, decline);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+
+ verify(mRepository).markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR));
+ }
+
+ private int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
+ return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray();
+ }
+
+ private void updateServingParams(Set<Inet4Address> defaultRouters,
+ Set<Inet4Address> dnsServers, Set<Inet4Address> excludedAddrs, LinkAddress serverAddr,
+ boolean changePrefixOnDecline) throws Exception {
+ final DhcpServingParamsParcel params = new DhcpServingParamsParcel();
+ params.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
+ params.serverAddrPrefixLength = serverAddr.getPrefixLength();
+ params.defaultRouters = toIntArray(defaultRouters);
+ params.dnsServers = toIntArray(dnsServers);
+ params.excludedAddrs = toIntArray(excludedAddrs);
+ params.dhcpLeaseTimeSecs = TEST_LEASE_EXPTIME_SECS;
+ params.linkMtu = TEST_MTU;
+ params.metered = true;
+ params.changePrefixOnDecline = changePrefixOnDecline;
+
+ mServer.updateParams(params, mAssertSuccessCallback);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ }
+
+ @Test
+ public void testChangePrefixOnDecline() throws Exception {
+ when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
+ eq(TEST_CLIENT_ADDR))).thenReturn(true);
+
+ mServer.start(mAssertSuccessCallback, mEventCallbacks);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
+
+ // Enable changePrefixOnDecline
+ updateServingParams(TEST_DEFAULT_ROUTERS, TEST_DNS_SERVERS, TEST_EXCLUDED_ADDRS,
+ TEST_SERVER_LINKADDR, true /* changePrefixOnDecline */);
+
+ runOnReceivedDeclinePacket();
+ final IpPrefix servingPrefix = DhcpServingParams.makeIpPrefix(TEST_SERVER_LINKADDR);
+ verify(mEventCallbacks).onNewPrefixRequest(eq(servingPrefix));
+
+ final Inet4Address serverAddr = parseAddr("192.168.51.129");
+ final LinkAddress srvLinkAddr = new LinkAddress(serverAddr, 24);
+ final Set<Inet4Address> srvAddr = new HashSet<>(Collections.singletonList(serverAddr));
+ final Set<Inet4Address> excludedAddrs = new HashSet<>(
+ Arrays.asList(parseAddr("192.168.51.200"), parseAddr("192.168.51.201")));
+
+ // Simulate IpServer updates the serving params with a new prefix.
+ updateServingParams(srvAddr, srvAddr, excludedAddrs, srvLinkAddr,
+ true /* changePrefixOnDecline */);
+
+ final Inet4Address clientAddr = parseAddr("192.168.51.42");
+ final DhcpLease lease = new DhcpLease(null, TEST_CLIENT_MAC,
+ clientAddr, 24 /*prefixLen*/, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+ null /* hostname */);
+ when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
+ eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
+ .thenReturn(lease);
+
+ // Test discover packet
+ final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
+ (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
+ false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
+ mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
+ HandlerUtilsKt.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
+ assertResponseSentTo(clientAddr);
+ final DhcpOfferPacket packet = assertOffer(getPacket());
+ assertMatchesLease(packet, serverAddr, clientAddr, null);
+ }
+
/* TODO: add more tests once packet construction is refactored, including:
* - usage of giaddr
* - usage of broadcast bit
* - other request states (init-reboot/renewing/rebinding)
*/
- private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
+ private void assertMatchesLease(@NonNull DhcpPacket packet, @NonNull Inet4Address srvAddr,
+ @NonNull Inet4Address clientAddr, @Nullable String hostname) {
assertMatchesClient(packet);
assertFalse(packet.hasExplicitClientId());
- assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
- assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
+ assertEquals(srvAddr, packet.mServerIdentifier);
+ assertEquals(clientAddr, packet.mYourIp);
assertNotNull(packet.mLeaseTime);
assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
assertEquals(hostname, packet.mHostName);
}
+ private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
+ assertMatchesLease(packet, TEST_SERVER_ADDR, TEST_CLIENT_ADDR, hostname);
+ }
+
private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
assertMatchesTestLease(packet, null);
}