• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package android.net.dhcp;
18 
19 import static android.net.InetAddresses.parseNumericAddress;
20 import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
21 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
22 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
23 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
24 import static android.net.dhcp.DhcpServer.CMD_RECEIVE_PACKET;
25 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
26 import static android.net.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION;
27 
28 import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH;
29 
30 import static junit.framework.Assert.assertEquals;
31 import static junit.framework.Assert.assertFalse;
32 import static junit.framework.Assert.assertNotNull;
33 import static junit.framework.Assert.assertTrue;
34 
35 import static org.mockito.ArgumentMatchers.any;
36 import static org.mockito.ArgumentMatchers.eq;
37 import static org.mockito.ArgumentMatchers.isNull;
38 import static org.mockito.Mockito.doNothing;
39 import static org.mockito.Mockito.never;
40 import static org.mockito.Mockito.times;
41 import static org.mockito.Mockito.verify;
42 import static org.mockito.Mockito.when;
43 
44 import android.annotation.NonNull;
45 import android.annotation.Nullable;
46 import android.content.Context;
47 import android.net.INetworkStackStatusCallback;
48 import android.net.IpPrefix;
49 import android.net.LinkAddress;
50 import android.net.MacAddress;
51 import android.net.dhcp.DhcpLeaseRepository.InvalidAddressException;
52 import android.net.dhcp.DhcpLeaseRepository.OutOfAddressesException;
53 import android.net.dhcp.DhcpServer.Clock;
54 import android.net.dhcp.DhcpServer.Dependencies;
55 import android.net.util.SharedLog;
56 import android.os.ConditionVariable;
57 import android.testing.AndroidTestingRunner;
58 
59 import androidx.test.filters.SmallTest;
60 
61 import com.android.net.module.util.Inet4AddressUtils;
62 import com.android.testutils.HandlerUtils;
63 
64 import org.junit.After;
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.ArgumentCaptor;
69 import org.mockito.Captor;
70 import org.mockito.Mock;
71 import org.mockito.MockitoAnnotations;
72 
73 import java.net.Inet4Address;
74 import java.nio.ByteBuffer;
75 import java.util.Arrays;
76 import java.util.Collection;
77 import java.util.Collections;
78 import java.util.HashSet;
79 import java.util.Set;
80 
81 @RunWith(AndroidTestingRunner.class)
82 @SmallTest
83 public class DhcpServerTest {
84     private static final String TEST_IFACE = "testiface";
85 
86     private static final Inet4Address TEST_SERVER_ADDR = parseAddr("192.168.0.2");
87     private static final int TEST_PREFIX_LENGTH = 20;
88     private static final LinkAddress TEST_SERVER_LINKADDR = new LinkAddress(
89             TEST_SERVER_ADDR, TEST_PREFIX_LENGTH);
90     private static final Set<Inet4Address> TEST_DEFAULT_ROUTERS = new HashSet<>(
91             Arrays.asList(parseAddr("192.168.0.123"), parseAddr("192.168.0.124")));
92     private static final Set<Inet4Address> TEST_DNS_SERVERS = new HashSet<>(
93             Arrays.asList(parseAddr("192.168.0.126"), parseAddr("192.168.0.127")));
94     private static final Set<Inet4Address> TEST_EXCLUDED_ADDRS = new HashSet<>(
95             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
96     private static final long TEST_LEASE_TIME_SECS = 3600L;
97     private static final int TEST_MTU = 1500;
98     private static final String TEST_HOSTNAME = "testhostname";
99 
100     private static final int TEST_TRANSACTION_ID = 123;
101     private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
102     private static final MacAddress TEST_CLIENT_MAC = MacAddress.fromBytes(TEST_CLIENT_MAC_BYTES);
103     private static final Inet4Address TEST_CLIENT_ADDR = parseAddr("192.168.0.42");
104 
105     private static final long TEST_CLOCK_TIME = 1234L;
106     private static final int TEST_LEASE_EXPTIME_SECS = 3600;
107     private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
108             TEST_CLIENT_ADDR, TEST_PREFIX_LENGTH, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
109             null /* hostname */);
110     private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
111             TEST_CLIENT_ADDR, TEST_PREFIX_LENGTH, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
112             TEST_HOSTNAME);
113     private static final int TEST_TIMEOUT_MS = 10000;
114 
115     @NonNull @Mock
116     private Context mContext;
117     @NonNull @Mock
118     private Dependencies mDeps;
119     @NonNull @Mock
120     private DhcpLeaseRepository mRepository;
121     @NonNull @Mock
122     private Clock mClock;
123     @NonNull @Mock
124     private DhcpPacketListener mPacketListener;
125     @NonNull @Mock
126     private IDhcpEventCallbacks mEventCallbacks;
127 
128     @NonNull @Captor
129     private ArgumentCaptor<ByteBuffer> mSentPacketCaptor;
130     @NonNull @Captor
131     private ArgumentCaptor<Inet4Address> mResponseDstAddrCaptor;
132 
133     @NonNull
134     private MyDhcpServer mServer;
135 
136     @Nullable
137     private String mPrevShareClassloaderProp;
138 
139     private class MyDhcpServer extends DhcpServer {
140         private final ConditionVariable mCv = new ConditionVariable(false);
141 
MyDhcpServer(Context context, String ifName, DhcpServingParams params, SharedLog log, Dependencies deps)142         MyDhcpServer(Context context, String ifName, DhcpServingParams params, SharedLog log,
143                 Dependencies deps) {
144             super(context, ifName, params, log, deps);
145         }
146 
147         @Override
onQuitting()148         protected void onQuitting() {
149             super.onQuitting();
150             mCv.open();
151         }
152 
waitForShutdown()153         public void waitForShutdown() {
154             assertTrue(mCv.block(TEST_TIMEOUT_MS));
155         }
156     }
157 
158     private final INetworkStackStatusCallback mAssertSuccessCallback =
159             new INetworkStackStatusCallback.Stub() {
160         @Override
161         public void onStatusAvailable(int statusCode) {
162             assertEquals(STATUS_SUCCESS, statusCode);
163         }
164 
165         @Override
166         public int getInterfaceVersion() {
167             return this.VERSION;
168         }
169 
170         @Override
171         public String getInterfaceHash() {
172             return this.HASH;
173         }
174     };
175 
makeServingParams()176     private DhcpServingParams makeServingParams() throws Exception {
177         return new DhcpServingParams.Builder()
178                 .setDefaultRouters(TEST_DEFAULT_ROUTERS)
179                 .setDhcpLeaseTimeSecs(TEST_LEASE_TIME_SECS)
180                 .setDnsServers(TEST_DNS_SERVERS)
181                 .setServerAddr(TEST_SERVER_LINKADDR)
182                 .setLinkMtu(TEST_MTU)
183                 .setExcludedAddrs(TEST_EXCLUDED_ADDRS)
184                 .setChangePrefixOnDecline(false)
185                 .build();
186     }
187 
startServer()188     private void startServer() throws Exception {
189         mServer.start(mAssertSuccessCallback);
190         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
191     }
192 
193     @Before
setUp()194     public void setUp() throws Exception {
195         MockitoAnnotations.initMocks(this);
196 
197         when(mDeps.makeLeaseRepository(any(), any(), any())).thenReturn(mRepository);
198         when(mDeps.makeClock()).thenReturn(mClock);
199         when(mDeps.makePacketListener(any())).thenReturn(mPacketListener);
200         when(mDeps.isFeatureEnabled(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))).thenReturn(true);
201         doNothing().when(mDeps)
202                 .sendPacket(any(), mSentPacketCaptor.capture(), mResponseDstAddrCaptor.capture());
203         when(mClock.elapsedRealtime()).thenReturn(TEST_CLOCK_TIME);
204         when(mPacketListener.start()).thenReturn(true);
205 
206         mServer = new MyDhcpServer(mContext, TEST_IFACE, makeServingParams(),
207                 new SharedLog(DhcpServerTest.class.getSimpleName()), mDeps);
208     }
209 
210     @After
tearDown()211     public void tearDown() throws Exception {
212         verify(mRepository, never()).addLeaseCallbacks(eq(null));
213         mServer.stop(mAssertSuccessCallback);
214         mServer.waitForShutdown();
215         verify(mPacketListener, times(1)).stop();
216     }
217 
218     @Test
testStart()219     public void testStart() throws Exception {
220         startServer();
221 
222         verify(mPacketListener, times(1)).start();
223     }
224 
225     @Test
testStartWithCallbacks()226     public void testStartWithCallbacks() throws Exception {
227         mServer.start(mAssertSuccessCallback, mEventCallbacks);
228         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
229         verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
230     }
231 
232     @Test
testDiscover()233     public void testDiscover() throws Exception {
234         startServer();
235 
236         // TODO: refactor packet construction to eliminate unnecessary/confusing/duplicate fields
237         when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
238                 eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
239                 .thenReturn(TEST_LEASE);
240 
241         final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
242                 (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
243                 false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
244         mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
245         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
246 
247         assertResponseSentTo(TEST_CLIENT_ADDR);
248         final DhcpOfferPacket packet = assertOffer(getPacket());
249         assertMatchesTestLease(packet);
250     }
251 
252     @Test
testDiscover_RapidCommit()253     public void testDiscover_RapidCommit() throws Exception {
254         startServer();
255 
256         when(mDeps.isFeatureEnabled(eq(mContext), eq(DHCP_RAPID_COMMIT_VERSION))).thenReturn(true);
257         when(mRepository.getCommittedLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
258                 eq(INADDR_ANY) /* relayAddr */, isNull() /* hostname */)).thenReturn(TEST_LEASE);
259 
260         final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
261                 (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
262                 false /* broadcast */, INADDR_ANY /* srcIp */, true /* rapidCommit */);
263         mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
264         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
265 
266         assertResponseSentTo(TEST_CLIENT_ADDR);
267         final DhcpAckPacket packet = assertAck(getPacket());
268         assertMatchesTestLease(packet);
269     }
270 
271     @Test
testDiscover_OutOfAddresses()272     public void testDiscover_OutOfAddresses() throws Exception {
273         startServer();
274 
275         when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
276                 eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
277                 .thenThrow(new OutOfAddressesException("Test exception"));
278 
279         final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
280                 (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
281                 false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
282         mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
283         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
284 
285         assertResponseSentTo(INADDR_BROADCAST);
286         final DhcpNakPacket packet = assertNak(getPacket());
287         assertMatchesClient(packet);
288     }
289 
makeRequestSelectingPacket()290     private DhcpRequestPacket makeRequestSelectingPacket() {
291         final DhcpRequestPacket request = new DhcpRequestPacket(TEST_TRANSACTION_ID,
292                 (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* relayIp */,
293                 TEST_CLIENT_MAC_BYTES, false /* broadcast */);
294         request.mServerIdentifier = TEST_SERVER_ADDR;
295         request.mRequestedIp = TEST_CLIENT_ADDR;
296         return request;
297     }
298 
299     @Test
testRequest_Selecting_Ack()300     public void testRequest_Selecting_Ack() throws Exception {
301         startServer();
302 
303         when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
304                 eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
305                 eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
306                 .thenReturn(TEST_LEASE_WITH_HOSTNAME);
307 
308         final DhcpRequestPacket request = makeRequestSelectingPacket();
309         request.mHostName = TEST_HOSTNAME;
310         request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
311         mServer.sendMessage(CMD_RECEIVE_PACKET, request);
312         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
313 
314         assertResponseSentTo(TEST_CLIENT_ADDR);
315         final DhcpAckPacket packet = assertAck(getPacket());
316         assertMatchesTestLease(packet, TEST_HOSTNAME);
317     }
318 
319     @Test
testRequest_Selecting_Nak()320     public void testRequest_Selecting_Nak() throws Exception {
321         startServer();
322 
323         when(mRepository.requestLease(isNull(), eq(TEST_CLIENT_MAC),
324                 eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
325                 eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
326                 .thenThrow(new InvalidAddressException("Test error"));
327 
328         final DhcpRequestPacket request = makeRequestSelectingPacket();
329         mServer.sendMessage(CMD_RECEIVE_PACKET, request);
330         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
331 
332         assertResponseSentTo(INADDR_BROADCAST);
333         final DhcpNakPacket packet = assertNak(getPacket());
334         assertMatchesClient(packet);
335     }
336 
337     @Test
testRelease()338     public void testRelease() throws Exception {
339         startServer();
340 
341         final DhcpReleasePacket release = new DhcpReleasePacket(TEST_TRANSACTION_ID,
342                 TEST_SERVER_ADDR, TEST_CLIENT_ADDR,
343                 INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES);
344         mServer.sendMessage(CMD_RECEIVE_PACKET, release);
345         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
346 
347         verify(mRepository, times(1))
348                 .releaseLease(isNull(), eq(TEST_CLIENT_MAC), eq(TEST_CLIENT_ADDR));
349     }
350 
351     @Test
testDecline_LeaseDoesNotExist()352     public void testDecline_LeaseDoesNotExist() throws Exception {
353         when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
354                 eq(TEST_CLIENT_ADDR))).thenReturn(false);
355 
356         startServer();
357         runOnReceivedDeclinePacket();
358         verify(mEventCallbacks, never()).onNewPrefixRequest(any());
359     }
360 
runOnReceivedDeclinePacket()361     private void runOnReceivedDeclinePacket() throws Exception {
362         when(mRepository.getCommittedLeases()).thenReturn(
363                 Arrays.asList(new DhcpLease(null, TEST_CLIENT_MAC, TEST_CLIENT_ADDR,
364                         TEST_PREFIX_LENGTH, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
365                         TEST_HOSTNAME)));
366         final DhcpDeclinePacket decline = new DhcpDeclinePacket(TEST_TRANSACTION_ID,
367                 (short) 0 /* secs */, INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */,
368                 INADDR_ANY /* nextIp */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
369                 TEST_CLIENT_ADDR /* requestedIp */, TEST_SERVER_ADDR /* serverIdentifier */);
370         mServer.sendMessage(CMD_RECEIVE_PACKET, decline);
371         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
372 
373         verify(mRepository).markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
374                 eq(TEST_CLIENT_ADDR));
375     }
376 
toIntArray(@onNull Collection<Inet4Address> addrs)377     private int[] toIntArray(@NonNull Collection<Inet4Address> addrs) {
378         return addrs.stream().mapToInt(Inet4AddressUtils::inet4AddressToIntHTH).toArray();
379     }
380 
updateServingParams(Set<Inet4Address> defaultRouters, Set<Inet4Address> dnsServers, Set<Inet4Address> excludedAddrs, LinkAddress serverAddr, boolean changePrefixOnDecline)381     private void updateServingParams(Set<Inet4Address> defaultRouters,
382             Set<Inet4Address> dnsServers, Set<Inet4Address> excludedAddrs, LinkAddress serverAddr,
383             boolean changePrefixOnDecline) throws Exception {
384         final DhcpServingParamsParcel params = new DhcpServingParamsParcel();
385         params.serverAddr = inet4AddressToIntHTH((Inet4Address) serverAddr.getAddress());
386         params.serverAddrPrefixLength = serverAddr.getPrefixLength();
387         params.defaultRouters = toIntArray(defaultRouters);
388         params.dnsServers = toIntArray(dnsServers);
389         params.excludedAddrs = toIntArray(excludedAddrs);
390         params.dhcpLeaseTimeSecs = TEST_LEASE_EXPTIME_SECS;
391         params.linkMtu = TEST_MTU;
392         params.metered = true;
393         params.changePrefixOnDecline = changePrefixOnDecline;
394 
395         mServer.updateParams(params, mAssertSuccessCallback);
396         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
397     }
398 
399     @Test
testChangePrefixOnDecline()400     public void testChangePrefixOnDecline() throws Exception {
401         when(mRepository.markAndReleaseDeclinedLease(isNull(), eq(TEST_CLIENT_MAC),
402                 eq(TEST_CLIENT_ADDR))).thenReturn(true);
403 
404         mServer.start(mAssertSuccessCallback, mEventCallbacks);
405         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
406         verify(mRepository).addLeaseCallbacks(eq(mEventCallbacks));
407 
408         // Enable changePrefixOnDecline
409         updateServingParams(TEST_DEFAULT_ROUTERS, TEST_DNS_SERVERS, TEST_EXCLUDED_ADDRS,
410                 TEST_SERVER_LINKADDR, true /* changePrefixOnDecline */);
411 
412         runOnReceivedDeclinePacket();
413         final IpPrefix servingPrefix = DhcpServingParams.makeIpPrefix(TEST_SERVER_LINKADDR);
414         verify(mEventCallbacks).onNewPrefixRequest(eq(servingPrefix));
415 
416         final Inet4Address serverAddr = parseAddr("192.168.51.129");
417         final LinkAddress srvLinkAddr = new LinkAddress(serverAddr, 24);
418         final Set<Inet4Address> srvAddr = new HashSet<>(Collections.singletonList(serverAddr));
419         final Set<Inet4Address> excludedAddrs = new HashSet<>(
420                 Arrays.asList(parseAddr("192.168.51.200"), parseAddr("192.168.51.201")));
421 
422         // Simulate IpServer updates the serving params with a new prefix.
423         updateServingParams(srvAddr, srvAddr, excludedAddrs, srvLinkAddr,
424                 true /* changePrefixOnDecline */);
425 
426         final Inet4Address clientAddr = parseAddr("192.168.51.42");
427         final DhcpLease lease = new DhcpLease(null, TEST_CLIENT_MAC,
428                 clientAddr, 24 /*prefixLen*/, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
429                 null /* hostname */);
430         when(mRepository.getOffer(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
431                 eq(INADDR_ANY) /* relayAddr */, isNull() /* reqAddr */, isNull() /* hostname */))
432                 .thenReturn(lease);
433 
434         // Test discover packet
435         final DhcpDiscoverPacket discover = new DhcpDiscoverPacket(TEST_TRANSACTION_ID,
436                 (short) 0 /* secs */, INADDR_ANY /* relayIp */, TEST_CLIENT_MAC_BYTES,
437                 false /* broadcast */, INADDR_ANY /* srcIp */, false /* rapidCommit */);
438         mServer.sendMessage(CMD_RECEIVE_PACKET, discover);
439         HandlerUtils.waitForIdle(mServer.getHandler(), TEST_TIMEOUT_MS);
440         assertResponseSentTo(clientAddr);
441         final DhcpOfferPacket packet = assertOffer(getPacket());
442         assertMatchesLease(packet, serverAddr, clientAddr, null);
443     }
444 
445     /* TODO: add more tests once packet construction is refactored, including:
446      *  - usage of giaddr
447      *  - usage of broadcast bit
448      *  - other request states (init-reboot/renewing/rebinding)
449      */
450 
assertMatchesLease(@onNull DhcpPacket packet, @NonNull Inet4Address srvAddr, @NonNull Inet4Address clientAddr, @Nullable String hostname)451     private void assertMatchesLease(@NonNull DhcpPacket packet, @NonNull Inet4Address srvAddr,
452             @NonNull Inet4Address clientAddr, @Nullable String hostname) {
453         assertMatchesClient(packet);
454         assertFalse(packet.hasExplicitClientId());
455         assertEquals(srvAddr, packet.mServerIdentifier);
456         assertEquals(clientAddr, packet.mYourIp);
457         assertNotNull(packet.mLeaseTime);
458         assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
459         assertEquals(hostname, packet.mHostName);
460     }
461 
assertMatchesTestLease(@onNull DhcpPacket packet, @Nullable String hostname)462     private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
463         assertMatchesLease(packet, TEST_SERVER_ADDR, TEST_CLIENT_ADDR, hostname);
464     }
465 
assertMatchesTestLease(@onNull DhcpPacket packet)466     private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
467         assertMatchesTestLease(packet, null);
468     }
469 
assertMatchesClient(@onNull DhcpPacket packet)470     private void assertMatchesClient(@NonNull DhcpPacket packet) {
471         assertEquals(TEST_TRANSACTION_ID, packet.mTransId);
472         assertEquals(TEST_CLIENT_MAC, MacAddress.fromBytes(packet.mClientMac));
473     }
474 
assertResponseSentTo(@onNull Inet4Address addr)475     private void assertResponseSentTo(@NonNull Inet4Address addr) {
476         assertEquals(addr, mResponseDstAddrCaptor.getValue());
477     }
478 
assertNak(@ullable DhcpPacket packet)479     private static DhcpNakPacket assertNak(@Nullable DhcpPacket packet) {
480         assertTrue(packet instanceof DhcpNakPacket);
481         return (DhcpNakPacket) packet;
482     }
483 
assertAck(@ullable DhcpPacket packet)484     private static DhcpAckPacket assertAck(@Nullable DhcpPacket packet) {
485         assertTrue(packet instanceof DhcpAckPacket);
486         return (DhcpAckPacket) packet;
487     }
488 
assertOffer(@ullable DhcpPacket packet)489     private static DhcpOfferPacket assertOffer(@Nullable DhcpPacket packet) {
490         assertTrue(packet instanceof DhcpOfferPacket);
491         return (DhcpOfferPacket) packet;
492     }
493 
getPacket()494     private DhcpPacket getPacket() throws Exception {
495         verify(mDeps, times(1)).sendPacket(any(), any(), any());
496         return DhcpPacket.decodeFullPacket(mSentPacketCaptor.getValue(), ENCAP_BOOTP,
497                 new byte[0] /* optionsToSkip */);
498     }
499 
parseAddr(@ullable String inet4Addr)500     private static Inet4Address parseAddr(@Nullable String inet4Addr) {
501         return (Inet4Address) parseNumericAddress(inet4Addr);
502     }
503 }
504