Merge "Support specific client address configuration" am: 5fca2175db am: c6e1433cf0
Change-Id: I46b9b70fba4386b8af788c86d04825fbcc470f03
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/dhcp/DhcpServingParamsParcel.aidl
index a802e41..eb780a2 100644
--- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -25,4 +25,5 @@
long dhcpLeaseTimeSecs;
int linkMtu;
boolean metered;
+ int clientAddr;
}
diff --git a/common/networkstackclient/src/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/src/android/net/dhcp/DhcpServingParamsParcel.aidl
index 7b8b9ee..5e19374 100644
--- a/common/networkstackclient/src/android/net/dhcp/DhcpServingParamsParcel.aidl
+++ b/common/networkstackclient/src/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -26,5 +26,6 @@
long dhcpLeaseTimeSecs;
int linkMtu;
boolean metered;
+ int clientAddr;
}
diff --git a/src/android/net/dhcp/DhcpLeaseRepository.java b/src/android/net/dhcp/DhcpLeaseRepository.java
index 1dc2f7f..a1f6612 100644
--- a/src/android/net/dhcp/DhcpLeaseRepository.java
+++ b/src/android/net/dhcp/DhcpLeaseRepository.java
@@ -80,6 +80,8 @@
private int mSubnetMask;
private int mNumAddresses;
private long mLeaseTimeMs;
+ @Nullable
+ private Inet4Address mClientAddr;
/**
* Next timestamp when committed or declined leases should be checked for expired ones. This
@@ -128,21 +130,24 @@
private final LinkedHashMap<Inet4Address, Long> mDeclinedAddrs = new LinkedHashMap<>();
DhcpLeaseRepository(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
- long leaseTimeMs, @NonNull SharedLog log, @NonNull Clock clock) {
- updateParams(prefix, reservedAddrs, leaseTimeMs);
+ long leaseTimeMs, @Nullable Inet4Address clientAddr, @NonNull SharedLog log,
+ @NonNull Clock clock) {
mLog = log;
mClock = clock;
+ mClientAddr = clientAddr;
+ updateParams(prefix, reservedAddrs, leaseTimeMs, clientAddr);
}
public void updateParams(@NonNull IpPrefix prefix, @NonNull Set<Inet4Address> reservedAddrs,
- long leaseTimeMs) {
+ long leaseTimeMs, @Nullable Inet4Address clientAddr) {
mPrefix = prefix;
mReservedAddrs = Collections.unmodifiableSet(new HashSet<>(reservedAddrs));
mPrefixLength = prefix.getPrefixLength();
mSubnetMask = prefixLengthToV4NetmaskIntHTH(mPrefixLength);
mSubnetAddr = inet4AddressToIntHTH((Inet4Address) prefix.getAddress()) & mSubnetMask;
- mNumAddresses = 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength());
+ mNumAddresses = clientAddr != null ? 1 : 1 << (IPV4_ADDR_BITS - prefix.getPrefixLength());
mLeaseTimeMs = leaseTimeMs;
+ mClientAddr = clientAddr;
cleanMap(mDeclinedAddrs);
if (cleanMap(mCommittedLeases)) {
@@ -514,6 +519,9 @@
* address (with the ordering in {@link #getAddrIndex(int)}) is returned.
*/
private int getValidAddress(int addr) {
+ // Only mClientAddr is valid if static client address is enforced.
+ if (mClientAddr != null) return inet4AddressToIntHTH(mClientAddr);
+
final int lastByteMask = 0xff;
int addrIndex = getAddrIndex(addr); // 0-based index of the address in the subnet
diff --git a/src/android/net/dhcp/DhcpServer.java b/src/android/net/dhcp/DhcpServer.java
index 9c5b3c6..bf22fcb 100644
--- a/src/android/net/dhcp/DhcpServer.java
+++ b/src/android/net/dhcp/DhcpServer.java
@@ -205,8 +205,8 @@
@NonNull SharedLog log, @NonNull Clock clock) {
return new DhcpLeaseRepository(
DhcpServingParams.makeIpPrefix(servingParams.serverAddr),
- servingParams.excludedAddrs,
- servingParams.dhcpLeaseTimeSecs * 1000, log.forSubComponent(REPO_TAG), clock);
+ servingParams.excludedAddrs, servingParams.dhcpLeaseTimeSecs * 1000,
+ servingParams.clientAddr, log.forSubComponent(REPO_TAG), clock);
}
@Override
@@ -351,7 +351,8 @@
mLeaseRepo.updateParams(
DhcpServingParams.makeIpPrefix(mServingParams.serverAddr),
params.excludedAddrs,
- params.dhcpLeaseTimeSecs);
+ params.dhcpLeaseTimeSecs,
+ params.clientAddr);
cb = pair.second;
break;
diff --git a/src/android/net/dhcp/DhcpServingParams.java b/src/android/net/dhcp/DhcpServingParams.java
index eafe44e..63f847d 100644
--- a/src/android/net/dhcp/DhcpServingParams.java
+++ b/src/android/net/dhcp/DhcpServingParams.java
@@ -85,6 +85,12 @@
public final boolean metered;
/**
+ * Client inet address. This will be the only address offered by DhcpServer if set.
+ */
+ @Nullable
+ public final Inet4Address clientAddr;
+
+ /**
* Checked exception thrown when some parameters used to build {@link DhcpServingParams} are
* missing or invalid.
*/
@@ -97,7 +103,7 @@
private DhcpServingParams(@NonNull LinkAddress serverAddr,
@NonNull Set<Inet4Address> defaultRouters,
@NonNull Set<Inet4Address> dnsServers, @NonNull Set<Inet4Address> excludedAddrs,
- long dhcpLeaseTimeSecs, int linkMtu, boolean metered) {
+ long dhcpLeaseTimeSecs, int linkMtu, boolean metered, Inet4Address clientAddr) {
this.serverAddr = serverAddr;
this.defaultRouters = defaultRouters;
this.dnsServers = dnsServers;
@@ -105,6 +111,7 @@
this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs;
this.linkMtu = linkMtu;
this.metered = metered;
+ this.clientAddr = clientAddr;
}
/**
@@ -119,6 +126,11 @@
final LinkAddress serverAddr = new LinkAddress(
intToInet4AddressHTH(parcel.serverAddr),
parcel.serverAddrPrefixLength);
+ Inet4Address clientAddr = null;
+ if (parcel.clientAddr != 0) {
+ clientAddr = intToInet4AddressHTH(parcel.clientAddr);
+ }
+
return new Builder()
.setServerAddr(serverAddr)
.setDefaultRouters(toInet4AddressSet(parcel.defaultRouters))
@@ -127,6 +139,7 @@
.setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs)
.setLinkMtu(parcel.linkMtu)
.setMetered(parcel.metered)
+ .setClientAddr(clientAddr)
.build();
}
@@ -181,6 +194,7 @@
private long mDhcpLeaseTimeSecs;
private int mLinkMtu = MTU_UNSET;
private boolean mMetered;
+ private Inet4Address mClientAddr;
/**
* Set the server address and served prefix for the DHCP server.
@@ -305,6 +319,16 @@
}
/**
+ * Set the client address.
+ *
+ * <p>If not set, the default value is null.
+ */
+ public Builder setClientAddr(@Nullable Inet4Address clientAddr) {
+ this.mClientAddr = clientAddr;
+ return this;
+ }
+
+ /**
* Create a new {@link DhcpServingParams} instance based on parameters set in the builder.
*
* <p>This method has no side-effects. If it does not throw, a valid
@@ -358,7 +382,7 @@
Collections.unmodifiableSet(new HashSet<>(mDefaultRouters)),
Collections.unmodifiableSet(new HashSet<>(mDnsServers)),
Collections.unmodifiableSet(excl),
- mDhcpLeaseTimeSecs, mLinkMtu, mMetered);
+ mDhcpLeaseTimeSecs, mLinkMtu, mMetered, mClientAddr);
}
}
diff --git a/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java b/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
index 82f9b50..818a48a 100644
--- a/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpLeaseRepositoryTest.java
@@ -34,6 +34,7 @@
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
@@ -71,6 +72,7 @@
public class DhcpLeaseRepositoryTest {
private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
+ private static final Inet4Address TEST_CLIENT_ADDR = parseAddr4("192.168.42.2");
private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
new byte[] { 5, 4, 3, 2, 1, 0 });
@@ -108,12 +110,17 @@
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
+ initDhcpLeaseRepositoryWithOption(null);
+ }
+
+ private void initDhcpLeaseRepositoryWithOption(final Inet4Address clientAddr) {
+ reset(mCallbacks, mClock);
mLog = new SharedLog("DhcpLeaseRepositoryTest");
when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
// Use a non-null Binder for linkToDeath
when(mCallbacks.asBinder()).thenReturn(mCallbacksBinder);
mRepo = new DhcpLeaseRepository(
- TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
+ TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, clientAddr, mLog, mClock);
mRepo.addLeaseCallbacks(mCallbacks);
verify(mCallbacks, atLeastOnce()).asBinder();
}
@@ -145,7 +152,8 @@
@Test
public void testAddressExhaustion() throws Exception {
// Use a /28 to quickly run out of addresses
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS,
+ null /* clientAddr */);
// /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
requestAddresses((byte) 11);
@@ -191,7 +199,8 @@
// Update from /22 to /28 and add another reserved address
Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
newReserved.add(reservedAddr);
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS,
+ null /* clientAddr */);
// Callback is called for the second time with just this lease
verifyLeasesChangedCallback(2 /* times */, reqAddrIn28Lease);
verifyNoMoreInteractions(mCallbacks);
@@ -223,7 +232,7 @@
@Test
public void testUpdateParams_UsesNewPrefix() throws Exception {
final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
- mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+ mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS, null /* clientAddr */);
DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
@@ -313,6 +322,31 @@
assertNotEquals(invalidAddr, offer.getNetAddr());
}
+ @Test
+ public void testGetOffer_StaticClientAddress() throws Exception {
+ initDhcpLeaseRepositoryWithOption(TEST_CLIENT_ADDR);
+ final DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
+ IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
+ assertEquals(TEST_CLIENT_ADDR, offer.getNetAddr());
+ assertEquals(TEST_HOSTNAME_1, offer.getHostname());
+ }
+
+ @Test
+ public void testGetOffer_StaticClientAddressInUse() throws Exception {
+ initDhcpLeaseRepositoryWithOption(TEST_CLIENT_ADDR);
+ final byte[] clientId = new byte[] { 1 };
+ final DhcpLease lease = mRepo.requestLease(clientId, TEST_MAC_1,
+ IPV4_ADDR_ANY /* clientAddr */, IPV4_ADDR_ANY /* relayAddr */,
+ TEST_CLIENT_ADDR /* reqAddr */, false, TEST_HOSTNAME_1);
+
+ // Static client address only support single client use case.
+ try {
+ mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
+ INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
+ fail("Repository should be out of addresses and throw");
+ } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
+ }
+
@Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
public void testGetOffer_RelayInInvalidSubnet() throws Exception {
mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
@@ -507,7 +541,8 @@
@Test
public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
// Use a /28 to quickly run out of addresses
- mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
+ mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS,
+ null /* clientAddr */);
mRepo.markLeaseDeclined(TEST_INETADDR_1);
mRepo.markLeaseDeclined(TEST_INETADDR_2);
diff --git a/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java b/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
index 57a87a4..9948fe3 100644
--- a/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
+++ b/tests/unit/src/android/net/dhcp/DhcpServingParamsTest.java
@@ -33,11 +33,12 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.testutils.MiscAssertsKt;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.lang.reflect.Modifier;
import java.net.Inet4Address;
import java.util.Arrays;
import java.util.Collection;
@@ -56,6 +57,7 @@
private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>(
Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127")));
private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
+ private static final Inet4Address TEST_CLIENT_ADDR = parseAddr("192.168.0.42");
private static final LinkAddress TEST_LINKADDR = new LinkAddress(TEST_SERVER_ADDR, 20);
private static final int TEST_MTU = 1500;
private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
@@ -71,7 +73,8 @@
.setServerAddr(TEST_LINKADDR)
.setLinkMtu(TEST_MTU)
.setExcludedAddrs(TEST_EXCLUDED_ADDRS)
- .setMetered(TEST_METERED);
+ .setMetered(TEST_METERED)
+ .setClientAddr(TEST_CLIENT_ADDR);
}
@Test
@@ -178,6 +181,7 @@
parcel.linkMtu = TEST_MTU;
parcel.excludedAddrs = toIntArray(TEST_EXCLUDED_ADDRS);
parcel.metered = TEST_METERED;
+ parcel.clientAddr = inet4AddressToIntHTH(TEST_CLIENT_ADDR);
final DhcpServingParams parceled = DhcpServingParams.fromParcelableObject(parcel);
assertEquals(params.defaultRouters, parceled.defaultRouters);
@@ -187,12 +191,9 @@
assertEquals(params.linkMtu, parceled.linkMtu);
assertEquals(params.excludedAddrs, parceled.excludedAddrs);
assertEquals(params.metered, parceled.metered);
+ assertEquals(params.clientAddr, parceled.clientAddr);
- // Ensure that we do not miss any field if added in the future
- final long numFields = Arrays.stream(DhcpServingParams.class.getDeclaredFields())
- .filter(f -> !Modifier.isStatic(f.getModifiers()))
- .count();
- assertEquals(7, numFields);
+ MiscAssertsKt.assertFieldCountEquals(9, DhcpServingParamsParcel.class);
}
@Test(expected = InvalidParameterException.class)