blob: 4d98403bfd4e43b94e5fc65f4687d26df686aa6f [file] [log] [blame]
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +09001/*
2 * Copyright (C) 2015 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 VANe88516f2019-01-20 09:35:10 +090019import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
20import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
21import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
22import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
23import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_ACK;
24import static android.net.dhcp.DhcpPacket.DHCP_MESSAGE_TYPE_OFFER;
25import static android.net.dhcp.DhcpPacket.DHCP_MTU;
26import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
27import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
28import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
29import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
30import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
31import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
32import static android.net.dhcp.DhcpPacket.ENCAP_L2;
33import static android.net.dhcp.DhcpPacket.ENCAP_L3;
34import static android.net.dhcp.DhcpPacket.INADDR_ANY;
35import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
36import static android.net.dhcp.DhcpPacket.ParseException;
37import static android.net.shared.Inet4AddressUtils.getBroadcastAddress;
38import static android.net.shared.Inet4AddressUtils.getPrefixMaskAsInet4Address;
39
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090040import static org.junit.Assert.assertEquals;
41import static org.junit.Assert.assertNotNull;
42import static org.junit.Assert.assertNull;
43import static org.junit.Assert.assertTrue;
44import static org.junit.Assert.fail;
45
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090046import android.annotation.Nullable;
Lorenzo Colittid9735372015-06-02 13:15:50 +090047import android.net.DhcpResults;
Lorenzo Colittif68edb12015-06-02 17:46:24 +090048import android.net.LinkAddress;
Hugo Benichie0ea7fe2016-10-05 18:33:21 +090049import android.net.NetworkUtils;
50import android.net.metrics.DhcpErrorEvent;
Brett Chabot84151d92019-02-27 15:37:59 -080051
52import androidx.test.filters.SmallTest;
53import androidx.test.runner.AndroidJUnit4;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090054
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090055import com.android.internal.util.HexDump;
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090056
Remi NGUYEN VANe88516f2019-01-20 09:35:10 +090057import org.junit.Before;
58import org.junit.Test;
59import org.junit.runner.RunWith;
60
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090061import java.io.ByteArrayOutputStream;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090062import java.net.Inet4Address;
63import java.nio.ByteBuffer;
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090064import java.nio.charset.Charset;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090065import java.util.ArrayList;
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090066import java.util.Arrays;
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090067import java.util.Collections;
Hugo Benichi006e0612016-10-05 21:07:19 +090068import java.util.Random;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090069
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090070@RunWith(AndroidJUnit4.class)
71@SmallTest
72public class DhcpPacketTest {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090073
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090074 private static final Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
75 private static final Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
76 private static final int PREFIX_LENGTH = 22;
77 private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
78 private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
79 SERVER_ADDR, PREFIX_LENGTH);
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +090080 private static final String HOSTNAME = "testhostname";
81 private static final short MTU = 1500;
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090082 // Use our own empty address instead of IPV4_ADDR_ANY or INADDR_ANY to ensure that the code
Lorenzo Colittif68edb12015-06-02 17:46:24 +090083 // doesn't use == instead of equals when comparing addresses.
Remi NGUYEN VAN3ba6c0d2019-01-20 13:48:19 +090084 private static final Inet4Address ANY = v4Address("0.0.0.0");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090085
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +090086 private static final byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090087
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090088 private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
89 return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
90 }
91
Hugo Benichi4a0c5d72017-10-11 11:26:25 +090092 @Before
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090093 public void setUp() {
94 DhcpPacket.testOverrideVendorId = "android-dhcp-???";
95 DhcpPacket.testOverrideHostname = "android-01234567890abcde";
96 }
97
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090098 class TestDhcpPacket extends DhcpPacket {
99 private byte mType;
100 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900101 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900102
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900103 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
104 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +0900105 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900106 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900107 }
108
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900109 public TestDhcpPacket(byte type) {
110 this(type, INADDR_ANY, CLIENT_ADDR);
111 }
112
Lorenzo Colittid9735372015-06-02 13:15:50 +0900113 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900114 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900115 return this;
116 }
117
118 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900119 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +0900120 return this;
121 }
122
123 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
124 mLeaseTimeBytes = leaseTimeBytes;
125 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900126 }
127
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900128 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
129 mNetmaskBytes = netmaskBytes;
130 return this;
131 }
132
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900133 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
134 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
135 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
136 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
137 return result;
138 }
139
140 public void finishPacket(ByteBuffer buffer) {
141 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
142 if (mDomainBytes != null) {
143 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
144 }
145 if (mVendorInfoBytes != null) {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900146 addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900147 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900148 if (mLeaseTimeBytes != null) {
149 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
150 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900151 if (mNetmaskBytes != null) {
152 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
153 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900154 addTlvEnd(buffer);
155 }
156
157 // Convenience method.
158 public ByteBuffer build() {
159 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
160 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
161 pkt.flip();
162 return pkt;
163 }
164 }
165
166 private void assertDomainAndVendorInfoParses(
167 String expectedDomain, byte[] domainBytes,
Erik Kline496906e2015-09-16 15:44:54 +0900168 String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900169 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
170 .setDomainBytes(domainBytes)
171 .setVendorInfoBytes(vendorInfoBytes)
172 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900173 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
174 assertEquals(expectedDomain, offerPacket.mDomainName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900175 assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900176 }
177
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900178 @Test
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900179 public void testDomainName() throws Exception {
180 byte[] nullByte = new byte[] { 0x00 };
181 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
182 byte[] nonNullDomain = new byte[] {
183 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
184 };
185 byte[] trailingNullDomain = new byte[] {
186 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
187 };
188 byte[] embeddedNullsDomain = new byte[] {
189 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
190 };
191 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
192
193 byte[] meteredEmbeddedNull = metered.clone();
194 meteredEmbeddedNull[7] = (char) 0;
195
196 byte[] meteredTrailingNull = metered.clone();
197 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
198
199 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
200 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
201 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
202 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
203 "ANDROID\u0000METERED", meteredEmbeddedNull);
204 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
205 "ANDROID_METERE\u0000", meteredTrailingNull);
206 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900207
208 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
Erik Kline496906e2015-09-16 15:44:54 +0900209 long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900210 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
211 if (leaseTimeBytes != null) {
212 testPacket.setLeaseTimeBytes(leaseTimeBytes);
213 }
214 ByteBuffer packet = testPacket.build();
Erik Kline496906e2015-09-16 15:44:54 +0900215 DhcpPacket offerPacket = null;
216
Lorenzo Colittid9735372015-06-02 13:15:50 +0900217 if (!expectValid) {
Erik Kline496906e2015-09-16 15:44:54 +0900218 try {
219 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
220 fail("Invalid packet parsed successfully: " + offerPacket);
221 } catch (ParseException expected) {
222 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900223 return;
224 }
Erik Kline496906e2015-09-16 15:44:54 +0900225
226 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
227 assertNotNull(offerPacket);
Lorenzo Colittid9735372015-06-02 13:15:50 +0900228 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
229 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
230 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
231 }
232
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900233 @Test
Lorenzo Colittid9735372015-06-02 13:15:50 +0900234 public void testLeaseTime() throws Exception {
235 byte[] noLease = null;
236 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
237 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
238 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
239 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
240 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
241 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
242 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
243 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
244 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
245
246 assertLeaseTimeParses(true, null, 0, noLease);
247 assertLeaseTimeParses(false, null, 0, tooShortLease);
248 assertLeaseTimeParses(false, null, 0, tooLongLease);
249 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
250 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
251 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
252 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
253 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
254 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
255 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
256 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900257
258 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900259 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900260 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
261 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
262 }
263
264 private void checkIpAddress(String expected, byte type,
265 Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900266 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900267 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
268 .setNetmaskBytes(netmaskBytes)
269 .build();
270 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
271 DhcpResults results = offerPacket.toDhcpResults();
272
273 if (expected != null) {
274 LinkAddress expectedAddress = new LinkAddress(expected);
275 assertEquals(expectedAddress, results.ipAddress);
276 } else {
277 assertNull(results);
278 }
279 }
280
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900281 @Test
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900282 public void testIpAddress() throws Exception {
283 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
284 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
285 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900286 Inet4Address example1 = v4Address("192.0.2.1");
287 Inet4Address example2 = v4Address("192.0.2.43");
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900288
289 // A packet without any addresses is not valid.
290 checkIpAddress(null, ANY, ANY, slash24Netmask);
291
292 // ClientIP is used iff YourIP is not present.
293 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
294 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
295 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
296
297 // Invalid netmasks are ignored.
298 checkIpAddress(null, example2, ANY, invalidNetmask);
299
300 // If there is no netmask, implicit netmasks are used.
301 checkIpAddress("192.0.2.43/24", ANY, example2, null);
302 }
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900303
304 private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
305 String domains, String serverAddress, String vendorInfo, int leaseDuration,
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900306 boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900307 assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
308 assertEquals(v4Address(gateway), dhcpResults.gateway);
309
310 String[] dnsServerStrings = dnsServersString.split(",");
311 ArrayList dnsServers = new ArrayList();
312 for (String dnsServerString : dnsServerStrings) {
313 dnsServers.add(v4Address(dnsServerString));
314 }
315 assertEquals(dnsServers, dhcpResults.dnsServers);
316
317 assertEquals(domains, dhcpResults.domains);
318 assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
319 assertEquals(vendorInfo, dhcpResults.vendorInfo);
320 assertEquals(leaseDuration, dhcpResults.leaseDuration);
321 assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900322 assertEquals(mtu, dhcpResults.mtu);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900323 }
324
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900325 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900326 public void testOffer1() throws Exception {
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900327 // TODO: Turn all of these into golden files. This will probably require using
Brett Chabot84151d92019-02-27 15:37:59 -0800328 // androidx.test.InstrumentationRegistry for obtaining a Context object
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900329 // to read such golden files, along with an appropriate Android.mk.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900330 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900331 // IP header.
332 "451001480000000080118849c0a89003c0a89ff7" +
333 // UDP header.
334 "004300440134dcfa" +
335 // BOOTP header.
336 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
337 // MAC address.
338 "30766ff2a90c00000000000000000000" +
339 // Server name.
340 "0000000000000000000000000000000000000000000000000000000000000000" +
341 "0000000000000000000000000000000000000000000000000000000000000000" +
342 // File.
343 "0000000000000000000000000000000000000000000000000000000000000000" +
344 "0000000000000000000000000000000000000000000000000000000000000000" +
345 "0000000000000000000000000000000000000000000000000000000000000000" +
346 "0000000000000000000000000000000000000000000000000000000000000000" +
347 // Options
348 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900349 "3a0400000e103b040000189cff00000000000000000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900350
351 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
352 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
353 DhcpResults dhcpResults = offerPacket.toDhcpResults();
354 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900355 null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900356 }
357
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900358 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900359 public void testOffer2() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900360 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900361 // IP header.
362 "450001518d0600004011144dc0a82b01c0a82bf7" +
363 // UDP header.
364 "00430044013d9ac7" +
365 // BOOTP header.
366 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
367 // MAC address.
368 "30766ff2a90c00000000000000000000" +
369 // Server name.
370 "0000000000000000000000000000000000000000000000000000000000000000" +
371 "0000000000000000000000000000000000000000000000000000000000000000" +
372 // File.
373 "0000000000000000000000000000000000000000000000000000000000000000" +
374 "0000000000000000000000000000000000000000000000000000000000000000" +
375 "0000000000000000000000000000000000000000000000000000000000000000" +
376 "0000000000000000000000000000000000000000000000000000000000000000" +
377 // Options
378 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900379 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900380
381 assertEquals(337, packet.limit());
382 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
383 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
384 DhcpResults dhcpResults = offerPacket.toDhcpResults();
385 assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900386 null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900387 assertTrue(dhcpResults.hasMeteredHint());
388 }
389
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900390 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900391 public void testBadIpPacket() throws Exception {
392 final byte[] packet = HexDump.hexStringToByteArray(
393 // IP header.
394 "450001518d0600004011144dc0a82b01c0a82bf7");
395
396 try {
397 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
398 } catch (DhcpPacket.ParseException expected) {
399 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
400 return;
401 }
402 fail("Dhcp packet parsing should have failed");
403 }
404
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900405 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900406 public void testBadDhcpPacket() throws Exception {
407 final byte[] packet = HexDump.hexStringToByteArray(
408 // IP header.
409 "450001518d0600004011144dc0a82b01c0a82bf7" +
410 // UDP header.
411 "00430044013d9ac7" +
412 // BOOTP header.
413 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
414
415 try {
416 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
417 } catch (DhcpPacket.ParseException expected) {
418 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
419 return;
420 }
421 fail("Dhcp packet parsing should have failed");
422 }
423
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900424 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900425 public void testBadTruncatedOffer() throws Exception {
426 final byte[] packet = HexDump.hexStringToByteArray(
427 // IP header.
428 "450001518d0600004011144dc0a82b01c0a82bf7" +
429 // UDP header.
430 "00430044013d9ac7" +
431 // BOOTP header.
432 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
433 // MAC address.
434 "30766ff2a90c00000000000000000000" +
435 // Server name.
436 "0000000000000000000000000000000000000000000000000000000000000000" +
437 "0000000000000000000000000000000000000000000000000000000000000000" +
438 // File, missing one byte
439 "0000000000000000000000000000000000000000000000000000000000000000" +
440 "0000000000000000000000000000000000000000000000000000000000000000" +
441 "0000000000000000000000000000000000000000000000000000000000000000" +
442 "00000000000000000000000000000000000000000000000000000000000000");
443
444 try {
445 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
446 } catch (DhcpPacket.ParseException expected) {
447 assertDhcpErrorCodes(DhcpErrorEvent.L3_TOO_SHORT, expected.errorCode);
448 return;
449 }
450 fail("Dhcp packet parsing should have failed");
451 }
452
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900453 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900454 public void testBadOfferWithoutACookie() throws Exception {
455 final byte[] packet = HexDump.hexStringToByteArray(
456 // IP header.
457 "450001518d0600004011144dc0a82b01c0a82bf7" +
458 // UDP header.
459 "00430044013d9ac7" +
460 // BOOTP header.
461 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
462 // MAC address.
463 "30766ff2a90c00000000000000000000" +
464 // Server name.
465 "0000000000000000000000000000000000000000000000000000000000000000" +
466 "0000000000000000000000000000000000000000000000000000000000000000" +
467 // File.
468 "0000000000000000000000000000000000000000000000000000000000000000" +
469 "0000000000000000000000000000000000000000000000000000000000000000" +
470 "0000000000000000000000000000000000000000000000000000000000000000" +
471 "0000000000000000000000000000000000000000000000000000000000000000"
472 // No options
473 );
474
475 try {
476 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
477 } catch (DhcpPacket.ParseException expected) {
Hugo Benichi006e0612016-10-05 21:07:19 +0900478 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_NO_COOKIE, expected.errorCode);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900479 return;
480 }
481 fail("Dhcp packet parsing should have failed");
482 }
483
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900484 @Test
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900485 public void testOfferWithBadCookie() throws Exception {
486 final byte[] packet = HexDump.hexStringToByteArray(
487 // IP header.
488 "450001518d0600004011144dc0a82b01c0a82bf7" +
489 // UDP header.
490 "00430044013d9ac7" +
491 // BOOTP header.
492 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
493 // MAC address.
494 "30766ff2a90c00000000000000000000" +
495 // Server name.
496 "0000000000000000000000000000000000000000000000000000000000000000" +
497 "0000000000000000000000000000000000000000000000000000000000000000" +
498 // File.
499 "0000000000000000000000000000000000000000000000000000000000000000" +
500 "0000000000000000000000000000000000000000000000000000000000000000" +
501 "0000000000000000000000000000000000000000000000000000000000000000" +
502 "0000000000000000000000000000000000000000000000000000000000000000" +
503 // Bad cookie
504 "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
505 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
506
507 try {
508 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
509 } catch (DhcpPacket.ParseException expected) {
510 assertDhcpErrorCodes(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE, expected.errorCode);
511 return;
512 }
513 fail("Dhcp packet parsing should have failed");
514 }
515
516 private void assertDhcpErrorCodes(int expected, int got) {
517 assertEquals(Integer.toHexString(expected), Integer.toHexString(got));
518 }
519
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900520 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900521 public void testTruncatedOfferPackets() throws Exception {
522 final byte[] packet = HexDump.hexStringToByteArray(
523 // IP header.
524 "450001518d0600004011144dc0a82b01c0a82bf7" +
525 // UDP header.
526 "00430044013d9ac7" +
527 // BOOTP header.
528 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
529 // MAC address.
530 "30766ff2a90c00000000000000000000" +
531 // Server name.
532 "0000000000000000000000000000000000000000000000000000000000000000" +
533 "0000000000000000000000000000000000000000000000000000000000000000" +
534 // File.
535 "0000000000000000000000000000000000000000000000000000000000000000" +
536 "0000000000000000000000000000000000000000000000000000000000000000" +
537 "0000000000000000000000000000000000000000000000000000000000000000" +
538 "0000000000000000000000000000000000000000000000000000000000000000" +
539 // Options
540 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
541 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
542
543 for (int len = 0; len < packet.length; len++) {
544 try {
545 DhcpPacket.decodeFullPacket(packet, len, ENCAP_L3);
546 } catch (ParseException e) {
547 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
548 fail(String.format("bad truncated packet of length %d", len));
549 }
550 }
551 }
552 }
553
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900554 @Test
Hugo Benichi006e0612016-10-05 21:07:19 +0900555 public void testRandomPackets() throws Exception {
556 final int maxRandomPacketSize = 512;
557 final Random r = new Random();
558 for (int i = 0; i < 10000; i++) {
559 byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
560 r.nextBytes(packet);
561 try {
562 DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
563 } catch (ParseException e) {
564 if (e.errorCode == DhcpErrorEvent.PARSING_ERROR) {
565 fail("bad packet: " + HexDump.toHexString(packet));
566 }
567 }
568 }
569 }
570
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900571 private byte[] mtuBytes(int mtu) {
572 // 0x1a02: option 26, length 2. 0xff: no more options.
573 if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
574 throw new IllegalArgumentException(
575 String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
576 }
577 String hexString = String.format("1a02%04xff", mtu);
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900578 return HexDump.hexStringToByteArray(hexString);
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900579 }
580
581 private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
582 if (mtuBytes != null) {
583 packet.position(packet.capacity() - mtuBytes.length);
584 packet.put(mtuBytes);
585 packet.clear();
586 }
587 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
588 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
589 DhcpResults dhcpResults = offerPacket.toDhcpResults();
590 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
591 null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
592 }
593
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900594 @Test
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900595 public void testMtu() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900596 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900597 // IP header.
598 "451001480000000080118849c0a89003c0a89ff7" +
599 // UDP header.
600 "004300440134dcfa" +
601 // BOOTP header.
602 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
603 // MAC address.
604 "30766ff2a90c00000000000000000000" +
605 // Server name.
606 "0000000000000000000000000000000000000000000000000000000000000000" +
607 "0000000000000000000000000000000000000000000000000000000000000000" +
608 // File.
609 "0000000000000000000000000000000000000000000000000000000000000000" +
610 "0000000000000000000000000000000000000000000000000000000000000000" +
611 "0000000000000000000000000000000000000000000000000000000000000000" +
612 "0000000000000000000000000000000000000000000000000000000000000000" +
613 // Options
614 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900615 "3a0400000e103b040000189cff00000000"));
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900616
617 checkMtu(packet, 0, null);
618 checkMtu(packet, 0, mtuBytes(1501));
619 checkMtu(packet, 1500, mtuBytes(1500));
620 checkMtu(packet, 1499, mtuBytes(1499));
621 checkMtu(packet, 1280, mtuBytes(1280));
622 checkMtu(packet, 0, mtuBytes(1279));
623 checkMtu(packet, 0, mtuBytes(576));
624 checkMtu(packet, 0, mtuBytes(68));
625 checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
626 checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
627 checkMtu(packet, 0, mtuBytes(-1));
628 }
629
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900630 @Test
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900631 public void testBadHwaddrLength() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900632 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900633 // IP header.
634 "450001518d0600004011144dc0a82b01c0a82bf7" +
635 // UDP header.
636 "00430044013d9ac7" +
637 // BOOTP header.
638 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
639 // MAC address.
640 "30766ff2a90c00000000000000000000" +
641 // Server name.
642 "0000000000000000000000000000000000000000000000000000000000000000" +
643 "0000000000000000000000000000000000000000000000000000000000000000" +
644 // File.
645 "0000000000000000000000000000000000000000000000000000000000000000" +
646 "0000000000000000000000000000000000000000000000000000000000000000" +
647 "0000000000000000000000000000000000000000000000000000000000000000" +
648 "0000000000000000000000000000000000000000000000000000000000000000" +
649 // Options
650 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900651 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900652 String expectedClientMac = "30766FF2A90C";
653
654 final int hwAddrLenOffset = 20 + 8 + 2;
655 assertEquals(6, packet.get(hwAddrLenOffset));
656
657 // Expect the expected.
658 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
659 assertNotNull(offerPacket);
660 assertEquals(6, offerPacket.getClientMac().length);
661 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
662
663 // Reduce the hardware address length and verify that it shortens the client MAC.
664 packet.flip();
665 packet.put(hwAddrLenOffset, (byte) 5);
666 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
667 assertNotNull(offerPacket);
668 assertEquals(5, offerPacket.getClientMac().length);
669 assertEquals(expectedClientMac.substring(0, 10),
670 HexDump.toHexString(offerPacket.getClientMac()));
671
672 packet.flip();
673 packet.put(hwAddrLenOffset, (byte) 3);
674 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
675 assertNotNull(offerPacket);
676 assertEquals(3, offerPacket.getClientMac().length);
677 assertEquals(expectedClientMac.substring(0, 6),
678 HexDump.toHexString(offerPacket.getClientMac()));
679
680 // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
681 // and crash, and b) hardcode it to 6.
682 packet.flip();
683 packet.put(hwAddrLenOffset, (byte) -1);
684 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
685 assertNotNull(offerPacket);
686 assertEquals(6, offerPacket.getClientMac().length);
687 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
688
689 // Set the the hardware address length to a positive invalid value (> 16) and verify that we
690 // hardcode it to 6.
691 packet.flip();
692 packet.put(hwAddrLenOffset, (byte) 17);
693 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
694 assertNotNull(offerPacket);
695 assertEquals(6, offerPacket.getClientMac().length);
696 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
697 }
698
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900699 @Test
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900700 public void testPadAndOverloadedOptionsOffer() throws Exception {
701 // A packet observed in the real world that is interesting for two reasons:
702 //
703 // 1. It uses pad bytes, which we previously didn't support correctly.
704 // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
705 // store any information in the overloaded fields).
706 //
707 // For now, we just check that it parses correctly.
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900708 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900709 // Ethernet header.
710 "b4cef6000000e80462236e300800" +
711 // IP header.
712 "4500014c00000000ff11741701010101ac119876" +
713 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
714 "004300440138ae5a" +
715 // BOOTP header.
716 "020106000fa0059f0000000000000000ac1198760000000000000000" +
717 // MAC address.
718 "b4cef600000000000000000000000000" +
719 // Server name.
720 "ff00000000000000000000000000000000000000000000000000000000000000" +
721 "0000000000000000000000000000000000000000000000000000000000000000" +
722 // File.
723 "ff00000000000000000000000000000000000000000000000000000000000000" +
724 "0000000000000000000000000000000000000000000000000000000000000000" +
725 "0000000000000000000000000000000000000000000000000000000000000000" +
726 "0000000000000000000000000000000000000000000000000000000000000000" +
727 // Options
728 "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900729 "0000000000000000000000000000000000000000000000ff000000"));
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900730
731 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
732 assertTrue(offerPacket instanceof DhcpOfferPacket);
733 DhcpResults dhcpResults = offerPacket.toDhcpResults();
734 assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900735 null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900736 }
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900737
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900738 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900739 public void testBug2111() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900740 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900741 // IP header.
742 "4500014c00000000ff119beac3eaf3880a3f5d04" +
743 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
744 "0043004401387464" +
745 // BOOTP header.
746 "0201060002554812000a0000000000000a3f5d040000000000000000" +
747 // MAC address.
748 "00904c00000000000000000000000000" +
749 // Server name.
750 "0000000000000000000000000000000000000000000000000000000000000000" +
751 "0000000000000000000000000000000000000000000000000000000000000000" +
752 // File.
753 "0000000000000000000000000000000000000000000000000000000000000000" +
754 "0000000000000000000000000000000000000000000000000000000000000000" +
755 "0000000000000000000000000000000000000000000000000000000000000000" +
756 "0000000000000000000000000000000000000000000000000000000000000000" +
757 // Options.
758 "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900759 "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900760
761 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
762 assertTrue(offerPacket instanceof DhcpOfferPacket);
763 DhcpResults dhcpResults = offerPacket.toDhcpResults();
764 assertDhcpResults("10.63.93.4/20", "10.63.80.1", "192.0.2.1,192.0.2.2",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900765 "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900766 }
767
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900768 @Test
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900769 public void testBug2136() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900770 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900771 // Ethernet header.
772 "bcf5ac000000d0c7890000000800" +
773 // IP header.
774 "4500014c00000000ff119beac3eaf3880a3f5d04" +
775 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
776 "0043004401387574" +
777 // BOOTP header.
778 "0201060163339a3000050000000000000a209ecd0000000000000000" +
779 // MAC address.
780 "bcf5ac00000000000000000000000000" +
781 // Server name.
782 "0000000000000000000000000000000000000000000000000000000000000000" +
783 "0000000000000000000000000000000000000000000000000000000000000000" +
784 // File.
785 "0000000000000000000000000000000000000000000000000000000000000000" +
786 "0000000000000000000000000000000000000000000000000000000000000000" +
787 "0000000000000000000000000000000000000000000000000000000000000000" +
788 "0000000000000000000000000000000000000000000000000000000000000000" +
789 // Options.
790 "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900791 "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900792
793 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
794 assertTrue(offerPacket instanceof DhcpOfferPacket);
795 assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
796 DhcpResults dhcpResults = offerPacket.toDhcpResults();
797 assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900798 "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900799 }
Erik Klineb19238c2015-10-06 18:43:17 +0900800
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900801 @Test
Erik Klineb19238c2015-10-06 18:43:17 +0900802 public void testUdpServerAnySourcePort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900803 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Erik Klineb19238c2015-10-06 18:43:17 +0900804 // Ethernet header.
805 "9cd917000000001c2e0000000800" +
806 // IP header.
807 "45a00148000040003d115087d18194fb0a0f7af2" +
808 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
809 // NOTE: The server source port is not the canonical port 67.
810 "C29F004401341268" +
811 // BOOTP header.
812 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
813 // MAC address.
814 "9cd91700000000000000000000000000" +
815 // Server name.
816 "0000000000000000000000000000000000000000000000000000000000000000" +
817 "0000000000000000000000000000000000000000000000000000000000000000" +
818 // File.
819 "0000000000000000000000000000000000000000000000000000000000000000" +
820 "0000000000000000000000000000000000000000000000000000000000000000" +
821 "0000000000000000000000000000000000000000000000000000000000000000" +
822 "0000000000000000000000000000000000000000000000000000000000000000" +
823 // Options.
824 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900825 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Erik Klineb19238c2015-10-06 18:43:17 +0900826
827 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
828 assertTrue(offerPacket instanceof DhcpOfferPacket);
829 assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
830 DhcpResults dhcpResults = offerPacket.toDhcpResults();
831 assertDhcpResults("10.15.122.242/16", "10.15.200.23",
832 "209.129.128.3,209.129.148.3,209.129.128.6",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900833 "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
Erik Klineb19238c2015-10-06 18:43:17 +0900834 }
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900835
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900836 @Test
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900837 public void testUdpInvalidDstPort() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900838 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900839 // Ethernet header.
840 "9cd917000000001c2e0000000800" +
841 // IP header.
842 "45a00148000040003d115087d18194fb0a0f7af2" +
843 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
844 // NOTE: The destination port is a non-DHCP port.
845 "0043aaaa01341268" +
846 // BOOTP header.
847 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
848 // MAC address.
849 "9cd91700000000000000000000000000" +
850 // Server name.
851 "0000000000000000000000000000000000000000000000000000000000000000" +
852 "0000000000000000000000000000000000000000000000000000000000000000" +
853 // File.
854 "0000000000000000000000000000000000000000000000000000000000000000" +
855 "0000000000000000000000000000000000000000000000000000000000000000" +
856 "0000000000000000000000000000000000000000000000000000000000000000" +
857 "0000000000000000000000000000000000000000000000000000000000000000" +
858 // Options.
859 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900860 "d18180060f0777766d2e6564751c040a0fffffff000000"));
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900861
862 try {
863 DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
864 fail("Packet with invalid dst port did not throw ParseException");
865 } catch (ParseException expected) {}
866 }
867
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900868 @Test
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900869 public void testMultipleRouters() throws Exception {
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900870 final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900871 // Ethernet header.
872 "fc3d93000000" + "081735000000" + "0800" +
873 // IP header.
874 "45000148c2370000ff117ac2c0a8bd02ffffffff" +
875 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
876 "0043004401343beb" +
877 // BOOTP header.
878 "0201060027f518e20000800000000000c0a8bd310000000000000000" +
879 // MAC address.
880 "fc3d9300000000000000000000000000" +
881 // Server name.
882 "0000000000000000000000000000000000000000000000000000000000000000" +
883 "0000000000000000000000000000000000000000000000000000000000000000" +
884 // File.
885 "0000000000000000000000000000000000000000000000000000000000000000" +
886 "0000000000000000000000000000000000000000000000000000000000000000" +
887 "0000000000000000000000000000000000000000000000000000000000000000" +
888 "0000000000000000000000000000000000000000000000000000000000000000" +
889 // Options.
890 "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
Hugo Benichie0ea7fe2016-10-05 18:33:21 +0900891 "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900892
893 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
894 assertTrue(offerPacket instanceof DhcpOfferPacket);
895 assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
896 DhcpResults dhcpResults = offerPacket.toDhcpResults();
897 assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900898 null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900899 }
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900900
Hugo Benichi4a0c5d72017-10-11 11:26:25 +0900901 @Test
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900902 public void testDiscoverPacket() throws Exception {
903 short secs = 7;
904 int transactionId = 0xdeadbeef;
905 byte[] hwaddr = {
906 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
907 };
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900908
909 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
910 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
Erik Kline1d511ab2016-03-24 17:30:13 +0900911 false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900912
913 byte[] headers = new byte[] {
914 // Ethernet header.
915 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
916 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
917 (byte) 0x08, (byte) 0x00,
918 // IP header.
Erik Kline1d511ab2016-03-24 17:30:13 +0900919 (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900920 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
Erik Kline1d511ab2016-03-24 17:30:13 +0900921 (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900922 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
923 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
924 // UDP header.
925 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
Erik Kline1d511ab2016-03-24 17:30:13 +0900926 (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900927 // BOOTP.
928 (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
929 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
930 (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
931 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
932 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
933 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
934 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
935 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
936 (byte) 0xb1, (byte) 0x7a
937 };
938 byte[] options = new byte[] {
939 // Magic cookie 0x63825363.
940 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
941 // Message type DISCOVER.
942 (byte) 0x35, (byte) 0x01, (byte) 0x01,
943 // Client identifier Ethernet, da:01:19:5b:b1:7a.
944 (byte) 0x3d, (byte) 0x07,
945 (byte) 0x01,
946 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
947 // Max message size 1500.
948 (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
949 // Version "android-dhcp-???".
950 (byte) 0x3c, (byte) 0x10,
951 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
952 // Hostname "android-01234567890abcde"
953 (byte) 0x0c, (byte) 0x18,
954 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
955 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
956 // Requested parameter list.
Erik Kline1d511ab2016-03-24 17:30:13 +0900957 (byte) 0x37, (byte) 0x0a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900958 DHCP_SUBNET_MASK,
959 DHCP_ROUTER,
960 DHCP_DNS_SERVER,
961 DHCP_DOMAIN_NAME,
962 DHCP_MTU,
Erik Kline1d511ab2016-03-24 17:30:13 +0900963 DHCP_BROADCAST_ADDRESS,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900964 DHCP_LEASE_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900965 DHCP_RENEWAL_TIME,
966 DHCP_REBINDING_TIME,
967 DHCP_VENDOR_INFO,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900968 // End options.
969 (byte) 0xff,
970 // Our packets are always of even length. TODO: find out why and possibly fix it.
971 (byte) 0x00
972 };
973 byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
974 assertTrue((expected.length & 1) == 0);
975 System.arraycopy(headers, 0, expected, 0, headers.length);
976 System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
977
978 byte[] actual = new byte[packet.limit()];
979 packet.get(actual);
980 String msg =
981 "Expected:\n " + Arrays.toString(expected) +
982 "\nActual:\n " + Arrays.toString(actual);
983 assertTrue(msg, Arrays.equals(expected, actual));
984 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900985
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +0900986 public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
987 throws Exception {
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +0900988 final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
989 final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
990 final int transactionId = 0xdeadbeef;
991
992 final ByteBuffer packet = DhcpPacket.buildOfferPacket(
993 DhcpPacket.ENCAP_BOOTP, transactionId, false /* broadcast */,
994 SERVER_ADDR, INADDR_ANY /* relayIp */, CLIENT_ADDR /* yourIp */,
995 CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
996 BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
997 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +0900998 SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
999 false /* metered */, MTU);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001000
1001 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1002 // BOOTP headers
1003 bos.write(new byte[] {
1004 (byte) 0x02, (byte) 0x01, (byte) 0x06, (byte) 0x00,
1005 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
1006 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1007 // ciaddr
1008 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
1009 });
1010 // yiaddr
1011 bos.write(CLIENT_ADDR.getAddress());
1012 // siaddr
1013 bos.write(SERVER_ADDR.getAddress());
1014 // giaddr
1015 bos.write(INADDR_ANY.getAddress());
1016 // chaddr
1017 bos.write(CLIENT_MAC);
1018
1019 // Padding
1020 bos.write(new byte[202]);
1021
1022 // Options
1023 bos.write(new byte[]{
1024 // Magic cookie 0x63825363.
1025 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
1026 // Message type OFFER.
1027 (byte) 0x35, (byte) 0x01, (byte) 0x02,
1028 });
1029 // Server ID
1030 bos.write(new byte[] { (byte) 0x36, (byte) 0x04 });
1031 bos.write(SERVER_ADDR.getAddress());
1032 // Lease time
1033 bos.write(new byte[] { (byte) 0x33, (byte) 0x04 });
1034 bos.write(intToByteArray(leaseTimeSecs));
1035 if (leaseTimeSecs != INFINITE_LEASE) {
1036 // Renewal time
1037 bos.write(new byte[]{(byte) 0x3a, (byte) 0x04});
1038 bos.write(intToByteArray(renewalTime));
1039 // Rebinding time
1040 bos.write(new byte[]{(byte) 0x3b, (byte) 0x04});
1041 bos.write(intToByteArray(rebindingTime));
1042 }
1043 // Subnet mask
1044 bos.write(new byte[] { (byte) 0x01, (byte) 0x04 });
1045 bos.write(NETMASK.getAddress());
1046 // Broadcast address
1047 bos.write(new byte[] { (byte) 0x1c, (byte) 0x04 });
1048 bos.write(BROADCAST_ADDR.getAddress());
1049 // Router
1050 bos.write(new byte[] { (byte) 0x03, (byte) 0x04 });
1051 bos.write(SERVER_ADDR.getAddress());
1052 // Nameserver
1053 bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
1054 bos.write(SERVER_ADDR.getAddress());
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001055 // Hostname
1056 if (hostname != null) {
1057 bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
1058 bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
1059 }
1060 // MTU
1061 bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
1062 bos.write(shortToByteArray(MTU));
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001063 // End options.
1064 bos.write(0xff);
1065
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001066 if ((bos.size() & 1) != 0) {
1067 bos.write(0x00);
1068 }
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001069
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001070 final byte[] expected = bos.toByteArray();
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001071 final byte[] actual = new byte[packet.limit()];
1072 packet.get(actual);
1073 final String msg = "Expected:\n " + HexDump.dumpHexString(expected) +
1074 "\nActual:\n " + HexDump.dumpHexString(actual);
1075 assertTrue(msg, Arrays.equals(expected, actual));
1076 }
1077
1078 @Test
1079 public void testOfferPacket() throws Exception {
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001080 checkBuildOfferPacket(3600, HOSTNAME);
1081 checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
1082 checkBuildOfferPacket(0x80000000, HOSTNAME);
1083 checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
1084 checkBuildOfferPacket(3600, null);
Remi NGUYEN VAN6557a242018-07-13 17:42:39 +09001085 }
1086
1087 private static byte[] intToByteArray(int val) {
1088 return ByteBuffer.allocate(4).putInt(val).array();
1089 }
Remi NGUYEN VANf90a92b2018-09-21 12:57:53 +09001090
1091 private static byte[] shortToByteArray(short val) {
1092 return ByteBuffer.allocate(2).putShort(val).array();
1093 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +09001094}