blob: 27d725540d345a1a6b4cf9763496cb07f8ac60ec [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
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090034import 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;
Brett Chabot84151d92019-02-27 15:37:59 -080040
41import androidx.test.filters.SmallTest;
42import androidx.test.runner.AndroidJUnit4;
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090043
44import org.junit.Before;
45import org.junit.Test;
46import org.junit.runner.RunWith;
47import org.mockito.Mock;
48import org.mockito.MockitoAnnotations;
49
Brett Chabot84151d92019-02-27 15:37:59 -080050import static java.lang.String.format;
51
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090052import java.net.Inet4Address;
53import java.util.Arrays;
54import java.util.Collections;
55import java.util.HashSet;
56import java.util.Set;
57
58@RunWith(AndroidJUnit4.class)
59@SmallTest
60public class DhcpLeaseRepositoryTest {
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +090061 private static final Inet4Address TEST_DEF_ROUTER = parseAddr4("192.168.42.247");
62 private static final Inet4Address TEST_SERVER_ADDR = parseAddr4("192.168.42.241");
63 private static final Inet4Address TEST_RESERVED_ADDR = parseAddr4("192.168.42.243");
64 private static final MacAddress TEST_MAC_1 = MacAddress.fromBytes(
65 new byte[] { 5, 4, 3, 2, 1, 0 });
66 private static final MacAddress TEST_MAC_2 = MacAddress.fromBytes(
67 new byte[] { 0, 1, 2, 3, 4, 5 });
68 private static final MacAddress TEST_MAC_3 = MacAddress.fromBytes(
69 new byte[] { 0, 1, 2, 3, 4, 6 });
70 private static final Inet4Address TEST_INETADDR_1 = parseAddr4("192.168.42.248");
71 private static final Inet4Address TEST_INETADDR_2 = parseAddr4("192.168.42.249");
72 private static final String TEST_HOSTNAME_1 = "hostname1";
73 private static final String TEST_HOSTNAME_2 = "hostname2";
74 private static final IpPrefix TEST_IP_PREFIX = new IpPrefix(TEST_SERVER_ADDR, 22);
75 private static final long TEST_TIME = 100L;
76 private static final int TEST_LEASE_TIME_MS = 3_600_000;
77 private static final Set<Inet4Address> TEST_EXCL_SET =
78 Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
79 TEST_SERVER_ADDR, TEST_DEF_ROUTER, TEST_RESERVED_ADDR)));
80
81 @NonNull
82 private SharedLog mLog;
83 @NonNull @Mock
84 private Clock mClock;
85 @NonNull
86 private DhcpLeaseRepository mRepo;
87
88 private static Inet4Address parseAddr4(String inet4Addr) {
89 return (Inet4Address) parseNumericAddress(inet4Addr);
90 }
91
92 @Before
93 public void setUp() {
94 MockitoAnnotations.initMocks(this);
95 mLog = new SharedLog("DhcpLeaseRepositoryTest");
96 when(mClock.elapsedRealtime()).thenReturn(TEST_TIME);
97 mRepo = new DhcpLeaseRepository(
98 TEST_IP_PREFIX, TEST_EXCL_SET, TEST_LEASE_TIME_MS, mLog, mClock);
99 }
100
101 /**
102 * Request a number of addresses through offer/request. Useful to test address exhaustion.
103 * @param nAddr Number of addresses to request.
104 */
105 private void requestAddresses(byte nAddr) throws Exception {
106 final HashSet<Inet4Address> addrs = new HashSet<>();
107 byte[] hwAddrBytes = new byte[] { 8, 4, 3, 2, 1, 0 };
108 for (byte i = 0; i < nAddr; i++) {
109 hwAddrBytes[5] = i;
110 MacAddress newMac = MacAddress.fromBytes(hwAddrBytes);
111 final String hostname = "host_" + i;
112 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, newMac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900113 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900114
115 assertNotNull(lease);
116 assertEquals(newMac, lease.getHwAddr());
117 assertEquals(hostname, lease.getHostname());
118 assertTrue(format("Duplicate address allocated: %s in %s", lease.getNetAddr(), addrs),
119 addrs.add(lease.getNetAddr()));
120
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900121 requestLeaseSelecting(newMac, lease.getNetAddr(), hostname);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900122 }
123 }
124
125 @Test
126 public void testAddressExhaustion() throws Exception {
127 // Use a /28 to quickly run out of addresses
128 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
129
130 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VAN73105e112018-12-21 16:17:09 +0900131 requestAddresses((byte) 11);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900132
133 try {
134 mRepo.getOffer(null, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900135 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900136 fail("Should be out of addresses");
137 } catch (DhcpLeaseRepository.OutOfAddressesException e) {
138 // Expected
139 }
140 }
141
142 @Test
143 public void testUpdateParams_LeaseCleanup() throws Exception {
144 // Inside /28:
145 final Inet4Address reqAddrIn28 = parseAddr4("192.168.42.242");
146 final Inet4Address declinedAddrIn28 = parseAddr4("192.168.42.245");
147
148 // Inside /28, but not available there (first address of the range)
149 final Inet4Address declinedFirstAddrIn28 = parseAddr4("192.168.42.240");
150
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900151 final DhcpLease reqAddrIn28Lease = requestLeaseSelecting(TEST_MAC_1, reqAddrIn28);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900152 mRepo.markLeaseDeclined(declinedAddrIn28);
153 mRepo.markLeaseDeclined(declinedFirstAddrIn28);
154
155 // Inside /22, but outside /28:
156 final Inet4Address reqAddrIn22 = parseAddr4("192.168.42.3");
157 final Inet4Address declinedAddrIn22 = parseAddr4("192.168.42.4");
158
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900159 final DhcpLease reqAddrIn22Lease = requestLeaseSelecting(TEST_MAC_3, reqAddrIn22);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900160 mRepo.markLeaseDeclined(declinedAddrIn22);
161
162 // Address that will be reserved in the updateParams call below
163 final Inet4Address reservedAddr = parseAddr4("192.168.42.244");
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900164 final DhcpLease reservedAddrLease = requestLeaseSelecting(TEST_MAC_2, reservedAddr);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900165
166 // Update from /22 to /28 and add another reserved address
167 Set<Inet4Address> newReserved = new HashSet<>(TEST_EXCL_SET);
168 newReserved.add(reservedAddr);
169 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), newReserved, TEST_LEASE_TIME_MS);
170
171 assertHasLease(reqAddrIn28Lease);
172 assertDeclined(declinedAddrIn28);
173
174 assertNotDeclined(declinedFirstAddrIn28);
175
176 assertNoLease(reqAddrIn22Lease);
177 assertNotDeclined(declinedAddrIn22);
178
179 assertNoLease(reservedAddrLease);
180 }
181
182 @Test
183 public void testGetOffer_StableAddress() throws Exception {
184 for (final MacAddress macAddr : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
185 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900186 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900187
188 // Same lease is offered twice
189 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, macAddr,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900190 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900191 assertEquals(lease, newLease);
192 }
193 }
194
195 @Test
196 public void testUpdateParams_UsesNewPrefix() throws Exception {
197 final IpPrefix newPrefix = new IpPrefix(parseAddr4("192.168.123.0"), 24);
198 mRepo.updateParams(newPrefix, TEST_EXCL_SET, TEST_LEASE_TIME_MS);
199
200 DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900201 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900202 assertTrue(newPrefix.contains(lease.getNetAddr()));
203 }
204
205 @Test
206 public void testGetOffer_ExistingLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900207 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900208
209 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900210 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900211 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
212 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
213 }
214
215 @Test
216 public void testGetOffer_ClientIdHasExistingLease() throws Exception {
217 final byte[] clientId = new byte[] { 1, 2 };
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900218 mRepo.requestLease(clientId, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
219 IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
220 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900221
222 // Different MAC, but same clientId
223 DhcpLease offer = mRepo.getOffer(clientId, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900224 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900225 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
226 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
227 }
228
229 @Test
230 public void testGetOffer_DifferentClientId() throws Exception {
231 final byte[] clientId1 = new byte[] { 1, 2 };
232 final byte[] clientId2 = new byte[] { 3, 4 };
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900233 mRepo.requestLease(clientId1, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
234 IPV4_ADDR_ANY /* relayAddr */, TEST_INETADDR_1 /* reqAddr */, false,
235 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900236
237 // Same MAC, different client ID
238 DhcpLease offer = mRepo.getOffer(clientId2, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900239 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900240 // Obtains a different address
241 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
242 assertEquals(HOSTNAME_NONE, offer.getHostname());
243 assertEquals(TEST_MAC_1, offer.getHwAddr());
244 }
245
246 @Test
247 public void testGetOffer_RequestedAddress() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900248 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900249 TEST_INETADDR_1 /* reqAddr */, TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900250 assertEquals(TEST_INETADDR_1, offer.getNetAddr());
251 assertEquals(TEST_HOSTNAME_1, offer.getHostname());
252 }
253
254 @Test
255 public void testGetOffer_RequestedAddressInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900256 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900257 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900258 TEST_INETADDR_1 /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900259 assertNotEquals(TEST_INETADDR_1, offer.getNetAddr());
260 }
261
262 @Test
263 public void testGetOffer_RequestedAddressReserved() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900264 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900265 TEST_RESERVED_ADDR /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900266 assertNotEquals(TEST_RESERVED_ADDR, offer.getNetAddr());
267 }
268
269 @Test
270 public void testGetOffer_RequestedAddressInvalid() throws Exception {
271 final Inet4Address invalidAddr = parseAddr4("192.168.42.0");
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900272 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900273 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900274 assertNotEquals(invalidAddr, offer.getNetAddr());
275 }
276
277 @Test
278 public void testGetOffer_RequestedAddressOutsideSubnet() throws Exception {
279 final Inet4Address invalidAddr = parseAddr4("192.168.254.2");
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900280 DhcpLease offer = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900281 invalidAddr /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900282 assertNotEquals(invalidAddr, offer.getNetAddr());
283 }
284
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900285 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900286 public void testGetOffer_RelayInInvalidSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900287 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1, parseAddr4("192.168.254.2") /* relayAddr */,
288 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900289 }
290
291 @Test
292 public void testRequestLease_SelectingTwice() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900293 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1,
294 TEST_HOSTNAME_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900295
296 // Second request from same client for a different address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900297 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_2,
298 TEST_HOSTNAME_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900299
300 assertEquals(TEST_INETADDR_1, lease1.getNetAddr());
301 assertEquals(TEST_HOSTNAME_1, lease1.getHostname());
302
303 assertEquals(TEST_INETADDR_2, lease2.getNetAddr());
304 assertEquals(TEST_HOSTNAME_2, lease2.getHostname());
305
306 // First address freed when client requested a different one: another client can request it
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900307 final DhcpLease lease3 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900308 assertEquals(TEST_INETADDR_1, lease3.getNetAddr());
309 }
310
311 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
312 public void testRequestLease_SelectingInvalid() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900313 requestLeaseSelecting(TEST_MAC_1, parseAddr4("192.168.254.5"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900314 }
315
316 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
317 public void testRequestLease_SelectingInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900318 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
319 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900320 }
321
322 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
323 public void testRequestLease_SelectingReserved() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900324 requestLeaseSelecting(TEST_MAC_1, TEST_RESERVED_ADDR);
325 }
326
327 @Test(expected = DhcpLeaseRepository.InvalidSubnetException.class)
328 public void testRequestLease_SelectingRelayInInvalidSubnet() throws Exception {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900329 mRepo.requestLease(CLIENTID_UNSPEC, TEST_MAC_1, IPV4_ADDR_ANY /* clientAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900330 parseAddr4("192.168.128.1") /* relayAddr */, TEST_INETADDR_1 /* reqAddr */,
331 true /* sidSet */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900332 }
333
334 @Test
335 public void testRequestLease_InitReboot() throws Exception {
336 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900337 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900338
339 final long newTime = TEST_TIME + 100;
340 when(mClock.elapsedRealtime()).thenReturn(newTime);
341
342 // init-reboot (sidSet == false): verify configuration
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900343 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900344 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
345 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
346 }
347
348 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
349 public void testRequestLease_InitRebootWrongAddr() throws Exception {
350 // Request address once
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900351 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900352 // init-reboot with different requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900353 requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900354 }
355
356 @Test
357 public void testRequestLease_InitRebootUnknownAddr() throws Exception {
358 // init-reboot with unknown requested address
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900359 final DhcpLease lease = requestLeaseInitReboot(TEST_MAC_1, TEST_INETADDR_2);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900360 // RFC2131 says we should not reply to accommodate other servers, but since we are
361 // authoritative we allow creating the lease to avoid issues with lost lease DB (same as
362 // dnsmasq behavior)
363 assertEquals(TEST_INETADDR_2, lease.getNetAddr());
364 }
365
366 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
367 public void testRequestLease_InitRebootWrongSubnet() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900368 requestLeaseInitReboot(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900369 }
370
371 @Test
372 public void testRequestLease_Renewing() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900373 requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900374
375 final long newTime = TEST_TIME + 100;
376 when(mClock.elapsedRealtime()).thenReturn(newTime);
377
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900378 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900379
380 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
381 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
382 }
383
384 @Test
385 public void testRequestLease_RenewingUnknownAddr() throws Exception {
386 final long newTime = TEST_TIME + 100;
387 when(mClock.elapsedRealtime()).thenReturn(newTime);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900388 final DhcpLease lease = requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900389 // Allows renewing an unknown address if available
390 assertEquals(TEST_INETADDR_1, lease.getNetAddr());
391 assertEquals(newTime + TEST_LEASE_TIME_MS, lease.getExpTime());
392 }
393
394 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
395 public void testRequestLease_RenewingAddrInUse() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900396 requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
397 requestLeaseRenewing(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900398 }
399
400 @Test(expected = DhcpLeaseRepository.InvalidAddressException.class)
401 public void testRequestLease_RenewingInvalidAddr() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900402 requestLeaseRenewing(TEST_MAC_1, parseAddr4("192.168.254.2"));
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900403 }
404
405 @Test
406 public void testReleaseLease() throws Exception {
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900407 final DhcpLease lease1 = requestLeaseSelecting(TEST_MAC_1, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900408
409 assertHasLease(lease1);
410 assertTrue(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
411 assertNoLease(lease1);
412
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900413 final DhcpLease lease2 = requestLeaseSelecting(TEST_MAC_2, TEST_INETADDR_1);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900414 assertEquals(TEST_INETADDR_1, lease2.getNetAddr());
415 }
416
417 @Test
418 public void testReleaseLease_UnknownLease() {
419 assertFalse(mRepo.releaseLease(CLIENTID_UNSPEC, TEST_MAC_1, TEST_INETADDR_1));
420 }
421
422 @Test
423 public void testReleaseLease_StableOffer() throws Exception {
424 for (MacAddress mac : new MacAddress[] { TEST_MAC_1, TEST_MAC_2, TEST_MAC_3 }) {
425 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900426 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900427
428 requestLeaseSelecting(mac, lease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900429 mRepo.releaseLease(CLIENTID_UNSPEC, mac, lease.getNetAddr());
430
431 // Same lease is offered after it was released
432 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, mac,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900433 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900434 assertEquals(lease.getNetAddr(), newLease.getNetAddr());
435 }
436 }
437
438 @Test
439 public void testMarkLeaseDeclined() throws Exception {
440 final DhcpLease lease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900441 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900442
443 mRepo.markLeaseDeclined(lease.getNetAddr());
444
445 // Same lease is not offered again
446 final DhcpLease newLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900447 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900448 assertNotEquals(lease.getNetAddr(), newLease.getNetAddr());
449 }
450
451 @Test
452 public void testMarkLeaseDeclined_UsedIfOutOfAddresses() throws Exception {
453 // Use a /28 to quickly run out of addresses
454 mRepo.updateParams(new IpPrefix(TEST_SERVER_ADDR, 28), TEST_EXCL_SET, TEST_LEASE_TIME_MS);
455
456 mRepo.markLeaseDeclined(TEST_INETADDR_1);
457 mRepo.markLeaseDeclined(TEST_INETADDR_2);
458
459 // /28 should have 16 addresses, 14 w/o the first/last, 11 w/o excluded addresses
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900460 requestAddresses((byte) 9);
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900461
462 // Last 2 addresses: addresses marked declined should be used
463 final DhcpLease firstLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_1,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900464 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_1);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900465 requestLeaseSelecting(TEST_MAC_1, firstLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900466
467 final DhcpLease secondLease = mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_2,
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900468 IPV4_ADDR_ANY /* relayAddr */, INETADDR_UNSPEC /* reqAddr */, TEST_HOSTNAME_2);
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900469 requestLeaseSelecting(TEST_MAC_2, secondLease.getNetAddr());
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900470
471 // Now out of addresses
472 try {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900473 mRepo.getOffer(CLIENTID_UNSPEC, TEST_MAC_3, IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900474 INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE);
475 fail("Repository should be out of addresses and throw");
476 } catch (DhcpLeaseRepository.OutOfAddressesException e) { /* expected */ }
477
478 assertEquals(TEST_INETADDR_1, firstLease.getNetAddr());
479 assertEquals(TEST_HOSTNAME_1, firstLease.getHostname());
480 assertEquals(TEST_INETADDR_2, secondLease.getNetAddr());
481 assertEquals(TEST_HOSTNAME_2, secondLease.getHostname());
482 }
483
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900484 private DhcpLease requestLease(@NonNull MacAddress macAddr, @NonNull Inet4Address clientAddr,
485 @Nullable Inet4Address reqAddr, @Nullable String hostname, boolean sidSet)
486 throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900487 return mRepo.requestLease(CLIENTID_UNSPEC, macAddr, clientAddr,
488 IPV4_ADDR_ANY /* relayAddr */,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900489 reqAddr, sidSet, hostname);
490 }
491
492 /**
493 * Request a lease simulating a client in the SELECTING state.
494 */
495 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
496 @NonNull Inet4Address reqAddr, @Nullable String hostname)
497 throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900498 return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, hostname,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900499 true /* sidSet */);
500 }
501
502 /**
503 * Request a lease simulating a client in the SELECTING state.
504 */
505 private DhcpLease requestLeaseSelecting(@NonNull MacAddress macAddr,
506 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
507 return requestLeaseSelecting(macAddr, reqAddr, HOSTNAME_NONE);
508 }
509
510 /**
511 * Request a lease simulating a client in the INIT-REBOOT state.
512 */
513 private DhcpLease requestLeaseInitReboot(@NonNull MacAddress macAddr,
514 @NonNull Inet4Address reqAddr) throws DhcpLeaseRepository.DhcpLeaseException {
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +0900515 return requestLease(macAddr, IPV4_ADDR_ANY /* clientAddr */, reqAddr, HOSTNAME_NONE,
Remi NGUYEN VANe1a1dcc2018-08-31 12:14:24 +0900516 false /* sidSet */);
517 }
518
519 /**
520 * Request a lease simulating a client in the RENEWING state.
521 */
522 private DhcpLease requestLeaseRenewing(@NonNull MacAddress macAddr,
523 @NonNull Inet4Address clientAddr) throws DhcpLeaseRepository.DhcpLeaseException {
524 // Renewing: clientAddr filled in, no reqAddr
525 return requestLease(macAddr, clientAddr, INETADDR_UNSPEC /* reqAddr */, HOSTNAME_NONE,
526 true /* sidSet */);
527 }
528
Remi NGUYEN VANeca5b4e2018-06-27 17:20:36 +0900529 private void assertNoLease(DhcpLease lease) {
530 assertFalse("Leases contain " + lease, mRepo.getCommittedLeases().contains(lease));
531 }
532
533 private void assertHasLease(DhcpLease lease) {
534 assertTrue("Leases do not contain " + lease, mRepo.getCommittedLeases().contains(lease));
535 }
536
537 private void assertNotDeclined(Inet4Address addr) {
538 assertFalse("Address is declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
539 }
540
541 private void assertDeclined(Inet4Address addr) {
542 assertTrue("Address is not declined: " + addr, mRepo.getDeclinedAddresses().contains(addr));
543 }
544}