blob: 51d50d9eb13abb96be6c6c3dafc343f9aae9b3f3 [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
24import static org.junit.Assert.assertEquals;
25import static org.junit.Assert.assertFalse;
26import static org.junit.Assert.assertNotEquals;
27import static org.junit.Assert.assertNotNull;
28import static org.junit.Assert.assertTrue;
29import static org.junit.Assert.fail;
30import static org.mockito.Mockito.when;
31
32import static java.lang.String.format;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090033
34import android.annotation.NonNull;
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +090035import android.annotation.Nullable;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090036import android.net.IpPrefix;
37import android.net.MacAddress;
Remi NGUYEN VANa13007a2018-08-13 15:54:27 +090038import android.net.dhcp.DhcpServer.Clock;
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +090039import android.net.util.SharedLog;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090040import android.support.test.filters.SmallTest;
41import android.support.test.runner.AndroidJUnit4;
42
43import org.junit.Before;
44import org.junit.Test;
45import org.junit.runner.RunWith;
46import org.mockito.Mock;
47import org.mockito.MockitoAnnotations;
48
49import java.net.Inet4Address;
50import java.util.Arrays;
51import java.util.Collections;
52import java.util.HashSet;
53import java.util.Set;
54
55@RunWith(AndroidJUnit4.class)
56@SmallTest
57public class DhcpLeaseRepositoryTest {
58 private static final Inet4Address INET4_ANY = (Inet4Address) Inet4Address.ANY;
59 private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
60 private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
61 private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
62 private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
63 new byte[] { 5, 4, 3, 2, 1, 0 });
64 private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
65 new byte[] { 0, 1, 2, 3, 4, 5 });
66 private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
67 new byte[] { 0, 1, 2, 3, 4, 6 });
68 private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
69 private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
70 private static final String TEST_HOSTNAME_1 = "hostname1";
71 private static final String TEST_HOSTNAME_2 = "hostname2";
72 private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
73 private static final long TEST_TIME = 100L;
74 private static final int TEST_LEASE_TIME_MS = 3_600_000;
75 private static final Set<Inet4Address> TEST_EXCL_SET =
76 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
77 TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
78
79 @NonNull
80 private SharedLog mLog;
81 @NonNull @Mock
82 private Clock mClock;
83 @NonNull
84 private DhcpLeaseRepository mRepo;
85
86 private static Inet4Address parseAddr4(String inet4Addr) {
87 return (Inet4Address) parseNumericAddress(inet4Addr);
88 }
89
90 @Before
91 public void setUp() {
92 MockitoAnnotations.initMocks(this);
93 mLog = new SharedLog("DhcpLeaseRepositoryTest");
94 when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
95 mRepo = new DhcpLeaseRepository(
96 TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
97 }
98
99 /**
100 * Request a number of addresses through offer/request. Useful to test address exhaustion.
101 * @param nAddr Number of addresses to request.
102 */
103 private void requestAddresses(byte nAddr) throws Exception {
104 final HashSet<Inet4Address> addrs = new HashSet<>();
105 byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
106 for (byte i = 0; i < nAddr; i++) {
107 hwAddrBytes[5] = i;
108 MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
109 final String hostname = "host_" + i;
110 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900111 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900112
113 assertNotNull(lease);
114 assertEquals(newMac, lease.getHwAddr());
115 assertEquals(hostname, lease.getHostname());
116 assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
117 addrs.add(lease.getNetAddr()));
118
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900119 requestLeaseSelecting(newMac, lease.getNetAddr(), hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900120 }
121 }
122
123 @Test
124 public void testAddressExhaustion() throws Exception {
125 // Use a /28 to quickly run out of addresses
126 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
127
128 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +0900129 requestAddresses((byte) 11);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900130
131 try {
132 mRepo.getOffer(null, TEST_MAC_2,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900133 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900134 fail("Should be out of addresses");
135 } catch (DhcpLeaseRepository.OutOfAddressesException e) {
136 // Expected
137 }
138 }
139
140 @Test
141 public void testUpdateParams_LeaseCleanup() throws Exception {
142 // Inside /28:
143 final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
144 final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
145
146 // Inside /28, but not available there (first address of the range)
147 final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
148
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900149 final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900150 mRepo.markLeaseDeclined(declinedAddrIn28);
151 mRepo.markLeaseDeclined(declinedFirstAddrIn28);
152
153 // Inside /22, but outside /28:
154 final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
155 final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
156
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900157 final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900158 mRepo.markLeaseDeclined(declinedAddrIn22);
159
160 // Address that will be reserved in the updateParams call below
161 final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900162 final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900163
164 // Update from /22 to /28 and add another reserved address
165 Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
166 newReserved.add(reservedAddr);
167 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
168
169 assertHasLease(reqAddrIn28Lease);
170 assertDeclined(declinedAddrIn28);
171
172 assertNotDeclined(declinedFirstAddrIn28);
173
174 assertNoLease(reqAddrIn22Lease);
175 assertNotDeclined(declinedAddrIn22);
176
177 assertNoLease(reservedAddrLease);
178 }
179
180 @Test
181 public void testGetOffer_StableAddress() throws Exception {
182 for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
183 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900184 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900185
186 // Same lease is offered twice
187 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900188 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900189 assertEquals(lease, newLease);
190 }
191 }
192
193 @Test
194 public void testUpdateParams_UsesNewPrefix() throws Exception {
195 final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
196 mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
197
198 DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900199 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900200 assertTrue(newPrefix.contains(lease.getNetAddr()));
201 }
202
203 @Test
204 public void testGetOffer_ExistingLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900205 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900206
207 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900208 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900209 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
210 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
211 }
212
213 @Test
214 public void testGetOffer_ClientIdHasExistingLease() throws Exception {
215 final byte[] clientId = new byte[] { 1, 2 };
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900216 mRepo.requestLease(clientId, TEST_MAC_1, INET4_ANY /* clientAddr */,
217 INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900218
219 // Different MAC, but same clientId
220 DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900221 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900222 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
223 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
224 }
225
226 @Test
227 public void testGetOffer_DifferentClientId() throws Exception {
228 final byte[] clientId1 = new byte[] { 1, 2 };
229 final byte[] clientId2 = new byte[] { 3, 4 };
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900230 mRepo.requestLease(clientId1, TEST_MAC_1, INET4_ANY /* clientAddr */,
231 INET4_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900232
233 // Same MAC, different client ID
234 DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900235 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900236 // Obtains a different address
237 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
238 assertEquals(HOSTNAME_NONE, offer.getHostname());
239 assertEquals(TEST_MAC_1, offer.getHwAddr());
240 }
241
242 @Test
243 public void testGetOffer_RequestedAddress() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900244 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
245 TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900246 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
247 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
248 }
249
250 @Test
251 public void testGetOffer_RequestedAddressInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900252 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
253 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, INET4_ANY /* relayAddr */,
254 TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900255 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
256 }
257
258 @Test
259 public void testGetOffer_RequestedAddressReserved() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900260 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
261 TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900262 assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
263 }
264
265 @Test
266 public void testGetOffer_RequestedAddressInvalid() throws Exception {
267 final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900268 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
269 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900270 assertNotEquals(invalidAddr, offer.getNetAddr());
271 }
272
273 @Test
274 public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
275 final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900276 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* relayAddr */,
277 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900278 assertNotEquals(invalidAddr, offer.getNetAddr());
279 }
280
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900281 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900282 public void testGetOffer_RelayInInvalidSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900283 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
284 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900285 }
286
287 @Test
288 public void testRequestLease_SelectingTwice() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900289 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1,
290 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900291
292 // Second request from same client for a different address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900293 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2,
294 TEST_HOSTNAME_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900295
296 assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
297 assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
298
299 assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
300 assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
301
302 // First address freed when client requested a different one: another client can request it
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900303 final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900304 assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
305 }
306
307 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
308 public void testRequestLease_SelectingInvalid() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900309 requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900310 }
311
312 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
313 public void testRequestLease_SelectingInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900314 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
315 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900316 }
317
318 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
319 public void testRequestLease_SelectingReserved() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900320 requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR);
321 }
322
323 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
324 public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
325 mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, INET4_ANY /* clientAddr */,
326 parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
327 true /* sidSet */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900328 }
329
330 @Test
331 public void testRequestLease_InitReboot() throws Exception {
332 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900333 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900334
335 final long newTime = TEST_TIME + 100;
336 when(mClock.elapsedRealtime()).thenReturn(newTime);
337
338 // init-reboot (sidSet == false): verify configuration
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900339 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900340 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
341 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
342 }
343
344 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
345 public void testRequestLease_InitRebootWrongAddr() throws Exception {
346 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900347 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900348 // init-reboot with different requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900349 requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900350 }
351
352 @Test
353 public void testRequestLease_InitRebootUnknownAddr() throws Exception {
354 // init-reboot with unknown requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900355 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900356 // RFC2131 says we should not reply to accommodate other servers, but since we are
357 // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
358 // dnsmasq behavior)
359 assertEquals(TEST_INETADDR_2, lease.getNetAddr());
360 }
361
362 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
363 public void testRequestLease_InitRebootWrongSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900364 requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900365 }
366
367 @Test
368 public void testRequestLease_Renewing() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900369 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900370
371 final long newTime = TEST_TIME + 100;
372 when(mClock.elapsedRealtime()).thenReturn(newTime);
373
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900374 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900375
376 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
377 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
378 }
379
380 @Test
381 public void testRequestLease_RenewingUnknownAddr() throws Exception {
382 final long newTime = TEST_TIME + 100;
383 when(mClock.elapsedRealtime()).thenReturn(newTime);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900384 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900385 // Allows renewing an unknown address if available
386 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
387 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
388 }
389
390 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
391 public void testRequestLease_RenewingAddrInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900392 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
393 requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900394 }
395
396 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
397 public void testRequestLease_RenewingInvalidAddr() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900398 requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900399 }
400
401 @Test
402 public void testReleaseLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900403 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900404
405 assertHasLease(lease1);
406 assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
407 assertNoLease(lease1);
408
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900409 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900410 assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
411 }
412
413 @Test
414 public void testReleaseLease_UnknownLease() {
415 assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
416 }
417
418 @Test
419 public void testReleaseLease_StableOffer() throws Exception {
420 for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
421 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900422 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
423
424 requestLeaseSelecting(mac, lease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900425 mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
426
427 // Same lease is offered after it was released
428 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900429 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900430 assertEquals(lease.getNetAddr(), newLease.getNetAddr());
431 }
432 }
433
434 @Test
435 public void testMarkLeaseDeclined() throws Exception {
436 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900437 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900438
439 mRepo.markLeaseDeclined(lease.getNetAddr());
440
441 // Same lease is not offered again
442 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900443 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900444 assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
445 }
446
447 @Test
448 public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
449 // Use a /28 to quickly run out of addresses
450 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
451
452 mRepo.markLeaseDeclined(TEST_INETADDR_1);
453 mRepo.markLeaseDeclined(TEST_INETADDR_2);
454
455 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900456 requestAddresses((byte) 9);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900457
458 // Last 2 addresses: addresses marked declined should be used
459 final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900460 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
461 requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900462
463 final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900464 INET4_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
465 requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900466
467 // Now out of addresses
468 try {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900469 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, INET4_ANY /* relayAddr */,
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900470 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
471 fail("Repository should be out of addresses and throw");
472 } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
473
474 assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
475 assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
476 assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
477 assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
478 }
479
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900480 private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
481 @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
482 throws DhcpLeaseRepository.DhcpLeaseException {
483 return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr, INET4_ANY /* relayAddr */,
484 reqAddr, sidSet, hostname);
485 }
486
487 /**
488 * Request a lease simulating a client in the SELECTING state.
489 */
490 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
491 @NonNull Inet4Address reqAddr, @Nullable String hostname)
492 throws DhcpLeaseRepository.DhcpLeaseException {
493 return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, hostname,
494 true /* sidSet */);
495 }
496
497 /**
498 * Request a lease simulating a client in the SELECTING state.
499 */
500 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
501 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
502 return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE);
503 }
504
505 /**
506 * Request a lease simulating a client in the INIT-REBOOT state.
507 */
508 private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
509 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
510 return requestLease(macAddr, INET4_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
511 false /* sidSet */);
512 }
513
514 /**
515 * Request a lease simulating a client in the RENEWING state.
516 */
517 private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr,
518 @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException {
519 // Renewing: clientAddr filled in, no reqAddr
520 return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE,
521 true /* sidSet */);
522 }
523
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900524 private void assertNoLease(DhcpLease lease) {
525 assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
526 }
527
528 private void assertHasLease(DhcpLease lease) {
529 assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
530 }
531
532 private void assertNotDeclined(Inet4Address addr) {
533 assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
534 }
535
536 private void assertDeclined(Inet4Address addr) {
537 assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
538 }
539}