blob: 4abd77e9cfdeec5cd8f239194c7596843cef1ba7 [file] [log] [blame]
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +09001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.dhcp;
18
Remi NGUYEN VAN0e3d09232018-12-04 12:13:09 +090019import static android.net.InetAddresses.parseNumericAddress;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090020import static android.net.dhcp.DhcpLease.HOSTNAME_NONE;
21import static android.net.dhcp.DhcpLeaseRepository.CLIENTID_UNSPEC;
22import static android.net.dhcp.DhcpLeaseRepository.INETADDR_UNSPEC;
23
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090024import static com.android.server.util.NetworkStackConstants.IPV4_ADDR_ANY;
25
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090026import static org.junit.Assert.assertEquals;
27import static org.junit.Assert.assertFalse;
28import static org.junit.Assert.assertNotEquals;
29import static org.junit.Assert.assertNotNull;
30import static org.junit.Assert.assertTrue;
31import static org.junit.Assert.fail;
32import static org.mockito.Mockito.when;
33
34import static java.lang.String.format;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090035
36import android.annotation.NonNull;
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +090037import android.annotation.Nullable;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090038import android.net.IpPrefix;
39import android.net.MacAddress;
Remi NGUYEN VANa13007a2018-08-13 15:54:27 +090040import android.net.dhcp.DhcpServer.Clock;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090041import android.net.util.SharedLog;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090042import android.support.test.filters.SmallTest;
43import android.support.test.runner.AndroidJUnit4;
44
45import org.junit.Before;
46import org.junit.Test;
47import org.junit.runner.RunWith;
48import org.mockito.Mock;
49import org.mockito.MockitoAnnotations;
50
51import java.net.Inet4Address;
52import java.util.Arrays;
53import java.util.Collections;
54import java.util.HashSet;
55import java.util.Set;
56
57@RunWith(AndroidJUnit4.class)
58@SmallTest
59public class DhcpLeaseRepositoryTest {
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090060 private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
61 private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
62 private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
63 private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
64 new byte[] { 5, 4, 3, 2, 1, 0 });
65 private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
66 new byte[] { 0, 1, 2, 3, 4, 5 });
67 private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
68 new byte[] { 0, 1, 2, 3, 4, 6 });
69 private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
70 private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
71 private static final String TEST_HOSTNAME_1 = "hostname1";
72 private static final String TEST_HOSTNAME_2 = "hostname2";
73 private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
74 private static final long TEST_TIME = 100L;
75 private static final int TEST_LEASE_TIME_MS = 3_600_000;
76 private static final Set<Inet4Address> TEST_EXCL_SET =
77 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
78 TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
79
80 @NonNull
81 private SharedLog mLog;
82 @NonNull @Mock
83 private Clock mClock;
84 @NonNull
85 private DhcpLeaseRepository mRepo;
86
87 private static Inet4Address parseAddr4(String inet4Addr) {
88 return (Inet4Address) parseNumericAddress(inet4Addr);
89 }
90
91 @Before
92 public void setUp() {
93 MockitoAnnotations.initMocks(this);
94 mLog = new SharedLog("DhcpLeaseRepositoryTest");
95 when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
96 mRepo = new DhcpLeaseRepository(
97 TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
98 }
99
100 /**
101 * Request a number of addresses through offer/request. Useful to test address exhaustion.
102 * @param nAddr Number of addresses to request.
103 */
104 private void requestAddresses(byte nAddr) throws Exception {
105 final HashSet<Inet4Address> addrs = new HashSet<>();
106 byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
107 for (byte i = 0; i < nAddr; i++) {
108 hwAddrBytes[5] = i;
109 MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
110 final String hostname = "host_" + i;
111 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900112 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900113
114 assertNotNull(lease);
115 assertEquals(newMac, lease.getHwAddr());
116 assertEquals(hostname, lease.getHostname());
117 assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
118 addrs.add(lease.getNetAddr()));
119
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900120 requestLeaseSelecting(newMac, lease.getNetAddr(), hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900121 }
122 }
123
124 @Test
125 public void testAddressExhaustion() throws Exception {
126 // Use a /28 to quickly run out of addresses
127 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
128
129 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +0900130 requestAddresses((byte) 11);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900131
132 try {
133 mRepo.getOffer(null, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900134 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900135 fail("Should be out of addresses");
136 } catch (DhcpLeaseRepository.OutOfAddressesException e) {
137 // Expected
138 }
139 }
140
141 @Test
142 public void testUpdateParams_LeaseCleanup() throws Exception {
143 // Inside /28:
144 final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
145 final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
146
147 // Inside /28, but not available there (first address of the range)
148 final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
149
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900150 final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900151 mRepo.markLeaseDeclined(declinedAddrIn28);
152 mRepo.markLeaseDeclined(declinedFirstAddrIn28);
153
154 // Inside /22, but outside /28:
155 final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
156 final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
157
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900158 final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900159 mRepo.markLeaseDeclined(declinedAddrIn22);
160
161 // Address that will be reserved in the updateParams call below
162 final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900163 final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900164
165 // Update from /22 to /28 and add another reserved address
166 Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
167 newReserved.add(reservedAddr);
168 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
169
170 assertHasLease(reqAddrIn28Lease);
171 assertDeclined(declinedAddrIn28);
172
173 assertNotDeclined(declinedFirstAddrIn28);
174
175 assertNoLease(reqAddrIn22Lease);
176 assertNotDeclined(declinedAddrIn22);
177
178 assertNoLease(reservedAddrLease);
179 }
180
181 @Test
182 public void testGetOffer_StableAddress() throws Exception {
183 for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
184 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900185 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900186
187 // Same lease is offered twice
188 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900189 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900190 assertEquals(lease, newLease);
191 }
192 }
193
194 @Test
195 public void testUpdateParams_UsesNewPrefix() throws Exception {
196 final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
197 mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
198
199 DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900200 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900201 assertTrue(newPrefix.contains(lease.getNetAddr()));
202 }
203
204 @Test
205 public void testGetOffer_ExistingLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900206 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900207
208 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900209 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900210 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
211 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
212 }
213
214 @Test
215 public void testGetOffer_ClientIdHasExistingLease() throws Exception {
216 final byte[] clientId = new byte[] { 1, 2 };
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900217 mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
218 IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
219 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900220
221 // Different MAC, but same clientId
222 DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900223 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900224 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
225 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
226 }
227
228 @Test
229 public void testGetOffer_DifferentClientId() throws Exception {
230 final byte[] clientId1 = new byte[] { 1, 2 };
231 final byte[] clientId2 = new byte[] { 3, 4 };
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900232 mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
233 IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
234 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900235
236 // Same MAC, different client ID
237 DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900238 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900239 // Obtains a different address
240 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
241 assertEquals(HOSTNAME_NONE, offer.getHostname());
242 assertEquals(TEST_MAC_1, offer.getHwAddr());
243 }
244
245 @Test
246 public void testGetOffer_RequestedAddress() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900247 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900248 TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900249 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
250 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
251 }
252
253 @Test
254 public void testGetOffer_RequestedAddressInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900255 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900256 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900257 TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900258 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
259 }
260
261 @Test
262 public void testGetOffer_RequestedAddressReserved() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900263 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900264 TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900265 assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
266 }
267
268 @Test
269 public void testGetOffer_RequestedAddressInvalid() throws Exception {
270 final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900271 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900272 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900273 assertNotEquals(invalidAddr, offer.getNetAddr());
274 }
275
276 @Test
277 public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
278 final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900279 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900280 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900281 assertNotEquals(invalidAddr, offer.getNetAddr());
282 }
283
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900284 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900285 public void testGetOffer_RelayInInvalidSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900286 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
287 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900288 }
289
290 @Test
291 public void testRequestLease_SelectingTwice() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900292 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1,
293 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900294
295 // Second request from same client for a different address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900296 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2,
297 TEST_HOSTNAME_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900298
299 assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
300 assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
301
302 assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
303 assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
304
305 // First address freed when client requested a different one: another client can request it
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900306 final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900307 assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
308 }
309
310 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
311 public void testRequestLease_SelectingInvalid() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900312 requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900313 }
314
315 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
316 public void testRequestLease_SelectingInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900317 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
318 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900319 }
320
321 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
322 public void testRequestLease_SelectingReserved() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900323 requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR);
324 }
325
326 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
327 public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900328 mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900329 parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
330 true /* sidSet */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900331 }
332
333 @Test
334 public void testRequestLease_InitReboot() throws Exception {
335 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900336 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900337
338 final long newTime = TEST_TIME + 100;
339 when(mClock.elapsedRealtime()).thenReturn(newTime);
340
341 // init-reboot (sidSet == false): verify configuration
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900342 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900343 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
344 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
345 }
346
347 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
348 public void testRequestLease_InitRebootWrongAddr() throws Exception {
349 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900350 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900351 // init-reboot with different requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900352 requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900353 }
354
355 @Test
356 public void testRequestLease_InitRebootUnknownAddr() throws Exception {
357 // init-reboot with unknown requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900358 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900359 // RFC2131 says we should not reply to accommodate other servers, but since we are
360 // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
361 // dnsmasq behavior)
362 assertEquals(TEST_INETADDR_2, lease.getNetAddr());
363 }
364
365 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
366 public void testRequestLease_InitRebootWrongSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900367 requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900368 }
369
370 @Test
371 public void testRequestLease_Renewing() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900372 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900373
374 final long newTime = TEST_TIME + 100;
375 when(mClock.elapsedRealtime()).thenReturn(newTime);
376
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900377 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900378
379 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
380 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
381 }
382
383 @Test
384 public void testRequestLease_RenewingUnknownAddr() throws Exception {
385 final long newTime = TEST_TIME + 100;
386 when(mClock.elapsedRealtime()).thenReturn(newTime);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900387 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900388 // Allows renewing an unknown address if available
389 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
390 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
391 }
392
393 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
394 public void testRequestLease_RenewingAddrInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900395 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
396 requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900397 }
398
399 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
400 public void testRequestLease_RenewingInvalidAddr() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900401 requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900402 }
403
404 @Test
405 public void testReleaseLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900406 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900407
408 assertHasLease(lease1);
409 assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
410 assertNoLease(lease1);
411
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900412 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900413 assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
414 }
415
416 @Test
417 public void testReleaseLease_UnknownLease() {
418 assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
419 }
420
421 @Test
422 public void testReleaseLease_StableOffer() throws Exception {
423 for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
424 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900425 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900426
427 requestLeaseSelecting(mac, lease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900428 mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
429
430 // Same lease is offered after it was released
431 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900432 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900433 assertEquals(lease.getNetAddr(), newLease.getNetAddr());
434 }
435 }
436
437 @Test
438 public void testMarkLeaseDeclined() throws Exception {
439 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900440 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900441
442 mRepo.markLeaseDeclined(lease.getNetAddr());
443
444 // Same lease is not offered again
445 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900446 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900447 assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
448 }
449
450 @Test
451 public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
452 // Use a /28 to quickly run out of addresses
453 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
454
455 mRepo.markLeaseDeclined(TEST_INETADDR_1);
456 mRepo.markLeaseDeclined(TEST_INETADDR_2);
457
458 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900459 requestAddresses((byte) 9);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900460
461 // Last 2 addresses: addresses marked declined should be used
462 final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900463 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900464 requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900465
466 final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900467 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900468 requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900469
470 // Now out of addresses
471 try {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900472 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900473 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
474 fail("Repository should be out of addresses and throw");
475 } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
476
477 assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
478 assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
479 assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
480 assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
481 }
482
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900483 private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
484 @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
485 throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900486 return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr,
487 IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900488 reqAddr, sidSet, hostname);
489 }
490
491 /**
492 * Request a lease simulating a client in the SELECTING state.
493 */
494 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
495 @NonNull Inet4Address reqAddr, @Nullable String hostname)
496 throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900497 return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900498 true /* sidSet */);
499 }
500
501 /**
502 * Request a lease simulating a client in the SELECTING state.
503 */
504 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
505 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
506 return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE);
507 }
508
509 /**
510 * Request a lease simulating a client in the INIT-REBOOT state.
511 */
512 private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
513 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900514 return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900515 false /* sidSet */);
516 }
517
518 /**
519 * Request a lease simulating a client in the RENEWING state.
520 */
521 private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr,
522 @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException {
523 // Renewing: clientAddr filled in, no reqAddr
524 return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE,
525 true /* sidSet */);
526 }
527
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900528 private void assertNoLease(DhcpLease lease) {
529 assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
530 }
531
532 private void assertHasLease(DhcpLease lease) {
533 assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
534 }
535
536 private void assertNotDeclined(Inet4Address addr) {
537 assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
538 }
539
540 private void assertDeclined(Inet4Address addr) {
541 assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
542 }
543}