diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2020-03-30 04:23:55 +0000 |
---|---|---|
committer | Mark Chien <markchien@google.com> | 2020-04-10 10:05:01 +0000 |
commit | fd1649fc1801a201af30ef03f7bf33c43085ee8b (patch) | |
tree | 3adc8be7c44b54bc1af74a4969d17eb75fa2d024 | |
parent | 6fb2a069f996e86c31998e0e0088cba5d311c7db (diff) |
Support specific client address configuration
If specific config "clientAddr" is set, the DHCP server will only offer
"clientAddr". This is for peer-to-peer use case. If there are multiple
clients for which "clientAddr" is already in use, dhcp server will not
offer other addresses to clients.
Bug: 141256482
Test: manual
atest NetworkStackNextTests
Merged-In: I96bc24a9c30bfc48dff38c3c4456085694fd381c
Change-Id: I96bc24a9c30bfc48dff38c3c4456085694fd381c
7 files changed, 92 insertions, 21 deletions
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 @@ parcelable DhcpServingParamsParcel { 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 @@ parcelable DhcpServingParamsParcel { 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 d0dc389..3639a2d 100644 --- a/src/android/net/dhcp/DhcpLeaseRepository.java +++ b/src/android/net/dhcp/DhcpLeaseRepository.java @@ -80,6 +80,8 @@ class DhcpLeaseRepository { 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 @@ class DhcpLeaseRepository { 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 @@ class DhcpLeaseRepository { * 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 064aa5d..429b10c 100644 --- a/src/android/net/dhcp/DhcpServer.java +++ b/src/android/net/dhcp/DhcpServer.java @@ -205,8 +205,8 @@ public class DhcpServer extends IDhcpServer.Stub { @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 @@ public class DhcpServer extends IDhcpServer.Stub { 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 class DhcpServingParams { 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 @@ public class DhcpServingParams { 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 @@ public class DhcpServingParams { this.dhcpLeaseTimeSecs = dhcpLeaseTimeSecs; this.linkMtu = linkMtu; this.metered = metered; + this.clientAddr = clientAddr; } /** @@ -119,6 +126,11 @@ public class DhcpServingParams { 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 @@ public class DhcpServingParams { .setDhcpLeaseTimeSecs(parcel.dhcpLeaseTimeSecs) .setLinkMtu(parcel.linkMtu) .setMetered(parcel.metered) + .setClientAddr(clientAddr) .build(); } @@ -181,6 +194,7 @@ public class DhcpServingParams { 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 @@ public class DhcpServingParams { } /** + * 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 @@ public class DhcpServingParams { 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 4cd0e6c..3a6a890 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.any; 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 @@ import java.util.Set; 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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { // 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 @@ public class DhcpLeaseRepositoryTest { @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 @@ public class DhcpLeaseRepositoryTest { 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 @@ public class DhcpLeaseRepositoryTest { @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 android.net.shared.Inet4AddressUtils; 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 @@ public class DhcpServingParamsTest { 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 @@ public class DhcpServingParamsTest { .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 @@ public class DhcpServingParamsTest { 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 @@ public class DhcpServingParamsTest { 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) |