blob: f8eaf7d0ee266b52c975fda26dfe5c1437a1e14c [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
19import android.net.NetworkUtils;
Lorenzo Colittid9735372015-06-02 13:15:50 +090020import android.net.DhcpResults;
Lorenzo Colittif68edb12015-06-02 17:46:24 +090021import android.net.LinkAddress;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090022import android.system.OsConstants;
23import android.test.suitebuilder.annotation.SmallTest;
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090024import com.android.internal.util.HexDump;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090025
26import java.net.Inet4Address;
27import java.nio.ByteBuffer;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090028import java.util.ArrayList;
29
Lorenzo Colittif28e62b2015-07-27 16:35:22 +090030import junit.framework.TestCase;
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090031import libcore.util.HexEncoding;
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090032import java.util.Arrays;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090033
34import static android.net.dhcp.DhcpPacket.*;
35
36
37public class DhcpPacketTest extends TestCase {
38
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090039 private static Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
40 private static Inet4Address CLIENT_ADDR = v4Address("192.0.2.234");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090041 // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
42 // doesn't use == instead of equals when comparing addresses.
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090043 private static Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090044
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090045 private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
46
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +090047 private static final Inet4Address v4Address(String addrString) throws IllegalArgumentException {
48 return (Inet4Address) NetworkUtils.numericToInetAddress(addrString);
49 }
50
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +090051 public void setUp() {
52 DhcpPacket.testOverrideVendorId = "android-dhcp-???";
53 DhcpPacket.testOverrideHostname = "android-01234567890abcde";
54 }
55
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090056 class TestDhcpPacket extends DhcpPacket {
57 private byte mType;
58 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +090059 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090060
Lorenzo Colittif68edb12015-06-02 17:46:24 +090061 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
62 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +090063 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090064 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +090065 }
66
Lorenzo Colittif68edb12015-06-02 17:46:24 +090067 public TestDhcpPacket(byte type) {
68 this(type, INADDR_ANY, CLIENT_ADDR);
69 }
70
Lorenzo Colittid9735372015-06-02 13:15:50 +090071 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090072 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090073 return this;
74 }
75
76 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090077 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090078 return this;
79 }
80
81 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
82 mLeaseTimeBytes = leaseTimeBytes;
83 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090084 }
85
Lorenzo Colittif68edb12015-06-02 17:46:24 +090086 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
87 mNetmaskBytes = netmaskBytes;
88 return this;
89 }
90
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090091 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
92 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
93 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
94 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
95 return result;
96 }
97
98 public void finishPacket(ByteBuffer buffer) {
99 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
100 if (mDomainBytes != null) {
101 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
102 }
103 if (mVendorInfoBytes != null) {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900104 addTlv(buffer, DHCP_VENDOR_INFO, mVendorInfoBytes);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900105 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900106 if (mLeaseTimeBytes != null) {
107 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
108 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900109 if (mNetmaskBytes != null) {
110 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
111 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900112 addTlvEnd(buffer);
113 }
114
115 // Convenience method.
116 public ByteBuffer build() {
117 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
118 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
119 pkt.flip();
120 return pkt;
121 }
122 }
123
124 private void assertDomainAndVendorInfoParses(
125 String expectedDomain, byte[] domainBytes,
Erik Kline496906e2015-09-16 15:44:54 +0900126 String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900127 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
128 .setDomainBytes(domainBytes)
129 .setVendorInfoBytes(vendorInfoBytes)
130 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900131 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
132 assertEquals(expectedDomain, offerPacket.mDomainName);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900133 assertEquals(expectedVendorInfo, offerPacket.mVendorInfo);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900134 }
135
136 @SmallTest
137 public void testDomainName() throws Exception {
138 byte[] nullByte = new byte[] { 0x00 };
139 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
140 byte[] nonNullDomain = new byte[] {
141 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
142 };
143 byte[] trailingNullDomain = new byte[] {
144 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
145 };
146 byte[] embeddedNullsDomain = new byte[] {
147 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
148 };
149 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
150
151 byte[] meteredEmbeddedNull = metered.clone();
152 meteredEmbeddedNull[7] = (char) 0;
153
154 byte[] meteredTrailingNull = metered.clone();
155 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
156
157 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
158 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
159 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
160 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
161 "ANDROID\u0000METERED", meteredEmbeddedNull);
162 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
163 "ANDROID_METERE\u0000", meteredTrailingNull);
164 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900165
166 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
Erik Kline496906e2015-09-16 15:44:54 +0900167 long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900168 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
169 if (leaseTimeBytes != null) {
170 testPacket.setLeaseTimeBytes(leaseTimeBytes);
171 }
172 ByteBuffer packet = testPacket.build();
Erik Kline496906e2015-09-16 15:44:54 +0900173 DhcpPacket offerPacket = null;
174
Lorenzo Colittid9735372015-06-02 13:15:50 +0900175 if (!expectValid) {
Erik Kline496906e2015-09-16 15:44:54 +0900176 try {
177 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
178 fail("Invalid packet parsed successfully: " + offerPacket);
179 } catch (ParseException expected) {
180 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900181 return;
182 }
Erik Kline496906e2015-09-16 15:44:54 +0900183
184 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
185 assertNotNull(offerPacket);
Lorenzo Colittid9735372015-06-02 13:15:50 +0900186 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
187 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
188 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
189 }
190
191 @SmallTest
192 public void testLeaseTime() throws Exception {
193 byte[] noLease = null;
194 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
195 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
196 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
197 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
198 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
199 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
200 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
201 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
202 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
203
204 assertLeaseTimeParses(true, null, 0, noLease);
205 assertLeaseTimeParses(false, null, 0, tooShortLease);
206 assertLeaseTimeParses(false, null, 0, tooLongLease);
207 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
208 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
209 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
210 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
211 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
212 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
213 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
214 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900215
216 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900217 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900218 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
219 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
220 }
221
222 private void checkIpAddress(String expected, byte type,
223 Inet4Address clientIp, Inet4Address yourIp,
Erik Kline496906e2015-09-16 15:44:54 +0900224 byte[] netmaskBytes) throws Exception {
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900225 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
226 .setNetmaskBytes(netmaskBytes)
227 .build();
228 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
229 DhcpResults results = offerPacket.toDhcpResults();
230
231 if (expected != null) {
232 LinkAddress expectedAddress = new LinkAddress(expected);
233 assertEquals(expectedAddress, results.ipAddress);
234 } else {
235 assertNull(results);
236 }
237 }
238
239 @SmallTest
240 public void testIpAddress() throws Exception {
241 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
242 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
243 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900244 Inet4Address example1 = v4Address("192.0.2.1");
245 Inet4Address example2 = v4Address("192.0.2.43");
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900246
247 // A packet without any addresses is not valid.
248 checkIpAddress(null, ANY, ANY, slash24Netmask);
249
250 // ClientIP is used iff YourIP is not present.
251 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
252 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
253 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
254
255 // Invalid netmasks are ignored.
256 checkIpAddress(null, example2, ANY, invalidNetmask);
257
258 // If there is no netmask, implicit netmasks are used.
259 checkIpAddress("192.0.2.43/24", ANY, example2, null);
260 }
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900261
262 private void assertDhcpResults(String ipAddress, String gateway, String dnsServersString,
263 String domains, String serverAddress, String vendorInfo, int leaseDuration,
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900264 boolean hasMeteredHint, int mtu, DhcpResults dhcpResults) throws Exception {
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900265 assertEquals(new LinkAddress(ipAddress), dhcpResults.ipAddress);
266 assertEquals(v4Address(gateway), dhcpResults.gateway);
267
268 String[] dnsServerStrings = dnsServersString.split(",");
269 ArrayList dnsServers = new ArrayList();
270 for (String dnsServerString : dnsServerStrings) {
271 dnsServers.add(v4Address(dnsServerString));
272 }
273 assertEquals(dnsServers, dhcpResults.dnsServers);
274
275 assertEquals(domains, dhcpResults.domains);
276 assertEquals(v4Address(serverAddress), dhcpResults.serverAddress);
277 assertEquals(vendorInfo, dhcpResults.vendorInfo);
278 assertEquals(leaseDuration, dhcpResults.leaseDuration);
279 assertEquals(hasMeteredHint, dhcpResults.hasMeteredHint());
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900280 assertEquals(mtu, dhcpResults.mtu);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900281 }
282
283 @SmallTest
284 public void testOffer1() throws Exception {
285 // TODO: Turn all of these into golden files. This will probably require modifying
286 // Android.mk appropriately, making this into an AndroidTestCase, and adding code to read
287 // the golden files from the test APK's assets via mContext.getAssets().
288 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
289 // IP header.
290 "451001480000000080118849c0a89003c0a89ff7" +
291 // UDP header.
292 "004300440134dcfa" +
293 // BOOTP header.
294 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
295 // MAC address.
296 "30766ff2a90c00000000000000000000" +
297 // Server name.
298 "0000000000000000000000000000000000000000000000000000000000000000" +
299 "0000000000000000000000000000000000000000000000000000000000000000" +
300 // File.
301 "0000000000000000000000000000000000000000000000000000000000000000" +
302 "0000000000000000000000000000000000000000000000000000000000000000" +
303 "0000000000000000000000000000000000000000000000000000000000000000" +
304 "0000000000000000000000000000000000000000000000000000000000000000" +
305 // Options
306 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
307 "3a0400000e103b040000189cff00000000000000000000"
308 ).toCharArray(), false));
309
310 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
311 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
312 DhcpResults dhcpResults = offerPacket.toDhcpResults();
313 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 +0900314 null, "192.168.144.3", null, 7200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900315 }
316
317 @SmallTest
318 public void testOffer2() throws Exception {
319 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
320 // IP header.
321 "450001518d0600004011144dc0a82b01c0a82bf7" +
322 // UDP header.
323 "00430044013d9ac7" +
324 // BOOTP header.
325 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
326 // MAC address.
327 "30766ff2a90c00000000000000000000" +
328 // Server name.
329 "0000000000000000000000000000000000000000000000000000000000000000" +
330 "0000000000000000000000000000000000000000000000000000000000000000" +
331 // File.
332 "0000000000000000000000000000000000000000000000000000000000000000" +
333 "0000000000000000000000000000000000000000000000000000000000000000" +
334 "0000000000000000000000000000000000000000000000000000000000000000" +
335 "0000000000000000000000000000000000000000000000000000000000000000" +
336 // Options
337 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
338 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
339 ).toCharArray(), false));
340
341 assertEquals(337, packet.limit());
342 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
343 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
344 DhcpResults dhcpResults = offerPacket.toDhcpResults();
345 assertDhcpResults("192.168.43.247/24", "192.168.43.1", "192.168.43.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900346 null, "192.168.43.1", "ANDROID_METERED", 3600, true, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900347 assertTrue(dhcpResults.hasMeteredHint());
348 }
349
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900350 private byte[] mtuBytes(int mtu) {
351 // 0x1a02: option 26, length 2. 0xff: no more options.
352 if (mtu > Short.MAX_VALUE - Short.MIN_VALUE) {
353 throw new IllegalArgumentException(
354 String.format("Invalid MTU %d, must be 16-bit unsigned", mtu));
355 }
356 String hexString = String.format("1a02%04xff", mtu);
357 return HexEncoding.decode(hexString.toCharArray(), false);
358 }
359
360 private void checkMtu(ByteBuffer packet, int expectedMtu, byte[] mtuBytes) throws Exception {
361 if (mtuBytes != null) {
362 packet.position(packet.capacity() - mtuBytes.length);
363 packet.put(mtuBytes);
364 packet.clear();
365 }
366 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
367 assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
368 DhcpResults dhcpResults = offerPacket.toDhcpResults();
369 assertDhcpResults("192.168.159.247/20", "192.168.159.254", "8.8.8.8,8.8.4.4",
370 null, "192.168.144.3", null, 7200, false, expectedMtu, dhcpResults);
371 }
372
373 @SmallTest
374 public void testMtu() throws Exception {
375 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
376 // IP header.
377 "451001480000000080118849c0a89003c0a89ff7" +
378 // UDP header.
379 "004300440134dcfa" +
380 // BOOTP header.
381 "02010600c997a63b0000000000000000c0a89ff70000000000000000" +
382 // MAC address.
383 "30766ff2a90c00000000000000000000" +
384 // Server name.
385 "0000000000000000000000000000000000000000000000000000000000000000" +
386 "0000000000000000000000000000000000000000000000000000000000000000" +
387 // File.
388 "0000000000000000000000000000000000000000000000000000000000000000" +
389 "0000000000000000000000000000000000000000000000000000000000000000" +
390 "0000000000000000000000000000000000000000000000000000000000000000" +
391 "0000000000000000000000000000000000000000000000000000000000000000" +
392 // Options
393 "638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
394 "3a0400000e103b040000189cff00000000"
395 ).toCharArray(), false));
396
397 checkMtu(packet, 0, null);
398 checkMtu(packet, 0, mtuBytes(1501));
399 checkMtu(packet, 1500, mtuBytes(1500));
400 checkMtu(packet, 1499, mtuBytes(1499));
401 checkMtu(packet, 1280, mtuBytes(1280));
402 checkMtu(packet, 0, mtuBytes(1279));
403 checkMtu(packet, 0, mtuBytes(576));
404 checkMtu(packet, 0, mtuBytes(68));
405 checkMtu(packet, 0, mtuBytes(Short.MIN_VALUE));
406 checkMtu(packet, 0, mtuBytes(Short.MAX_VALUE + 3));
407 checkMtu(packet, 0, mtuBytes(-1));
408 }
409
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900410 @SmallTest
Lorenzo Colittid64144a2015-09-03 17:36:20 +0900411 public void testBadHwaddrLength() throws Exception {
412 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
413 // IP header.
414 "450001518d0600004011144dc0a82b01c0a82bf7" +
415 // UDP header.
416 "00430044013d9ac7" +
417 // BOOTP header.
418 "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
419 // MAC address.
420 "30766ff2a90c00000000000000000000" +
421 // Server name.
422 "0000000000000000000000000000000000000000000000000000000000000000" +
423 "0000000000000000000000000000000000000000000000000000000000000000" +
424 // File.
425 "0000000000000000000000000000000000000000000000000000000000000000" +
426 "0000000000000000000000000000000000000000000000000000000000000000" +
427 "0000000000000000000000000000000000000000000000000000000000000000" +
428 "0000000000000000000000000000000000000000000000000000000000000000" +
429 // Options
430 "638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
431 "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
432 ).toCharArray(), false));
433 String expectedClientMac = "30766FF2A90C";
434
435 final int hwAddrLenOffset = 20 + 8 + 2;
436 assertEquals(6, packet.get(hwAddrLenOffset));
437
438 // Expect the expected.
439 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
440 assertNotNull(offerPacket);
441 assertEquals(6, offerPacket.getClientMac().length);
442 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
443
444 // Reduce the hardware address length and verify that it shortens the client MAC.
445 packet.flip();
446 packet.put(hwAddrLenOffset, (byte) 5);
447 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
448 assertNotNull(offerPacket);
449 assertEquals(5, offerPacket.getClientMac().length);
450 assertEquals(expectedClientMac.substring(0, 10),
451 HexDump.toHexString(offerPacket.getClientMac()));
452
453 packet.flip();
454 packet.put(hwAddrLenOffset, (byte) 3);
455 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
456 assertNotNull(offerPacket);
457 assertEquals(3, offerPacket.getClientMac().length);
458 assertEquals(expectedClientMac.substring(0, 6),
459 HexDump.toHexString(offerPacket.getClientMac()));
460
461 // Set the the hardware address length to 0xff and verify that we a) don't treat it as -1
462 // and crash, and b) hardcode it to 6.
463 packet.flip();
464 packet.put(hwAddrLenOffset, (byte) -1);
465 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
466 assertNotNull(offerPacket);
467 assertEquals(6, offerPacket.getClientMac().length);
468 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
469
470 // Set the the hardware address length to a positive invalid value (> 16) and verify that we
471 // hardcode it to 6.
472 packet.flip();
473 packet.put(hwAddrLenOffset, (byte) 17);
474 offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
475 assertNotNull(offerPacket);
476 assertEquals(6, offerPacket.getClientMac().length);
477 assertEquals(expectedClientMac, HexDump.toHexString(offerPacket.getClientMac()));
478 }
479
480 @SmallTest
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900481 public void testPadAndOverloadedOptionsOffer() throws Exception {
482 // A packet observed in the real world that is interesting for two reasons:
483 //
484 // 1. It uses pad bytes, which we previously didn't support correctly.
485 // 2. It uses DHCP option overloading, which we don't currently support (but it doesn't
486 // store any information in the overloaded fields).
487 //
488 // For now, we just check that it parses correctly.
489 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
490 // Ethernet header.
491 "b4cef6000000e80462236e300800" +
492 // IP header.
493 "4500014c00000000ff11741701010101ac119876" +
494 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
495 "004300440138ae5a" +
496 // BOOTP header.
497 "020106000fa0059f0000000000000000ac1198760000000000000000" +
498 // MAC address.
499 "b4cef600000000000000000000000000" +
500 // Server name.
501 "ff00000000000000000000000000000000000000000000000000000000000000" +
502 "0000000000000000000000000000000000000000000000000000000000000000" +
503 // File.
504 "ff00000000000000000000000000000000000000000000000000000000000000" +
505 "0000000000000000000000000000000000000000000000000000000000000000" +
506 "0000000000000000000000000000000000000000000000000000000000000000" +
507 "0000000000000000000000000000000000000000000000000000000000000000" +
508 // Options
509 "638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
510 "0000000000000000000000000000000000000000000000ff000000"
511 ).toCharArray(), false));
512
513 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
514 assertTrue(offerPacket instanceof DhcpOfferPacket);
515 DhcpResults dhcpResults = offerPacket.toDhcpResults();
516 assertDhcpResults("172.17.152.118/16", "172.17.1.1", "172.17.1.1",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900517 null, "1.1.1.1", null, 43200, false, 0, dhcpResults);
Lorenzo Colittib0b3d0b2015-07-06 12:29:43 +0900518 }
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900519
520 @SmallTest
521 public void testBug2111() throws Exception {
522 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
523 // IP header.
524 "4500014c00000000ff119beac3eaf3880a3f5d04" +
525 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
526 "0043004401387464" +
527 // BOOTP header.
528 "0201060002554812000a0000000000000a3f5d040000000000000000" +
529 // MAC address.
530 "00904c00000000000000000000000000" +
531 // Server name.
532 "0000000000000000000000000000000000000000000000000000000000000000" +
533 "0000000000000000000000000000000000000000000000000000000000000000" +
534 // File.
535 "0000000000000000000000000000000000000000000000000000000000000000" +
536 "0000000000000000000000000000000000000000000000000000000000000000" +
537 "0000000000000000000000000000000000000000000000000000000000000000" +
538 "0000000000000000000000000000000000000000000000000000000000000000" +
539 // Options.
540 "638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
541 "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"
542 ).toCharArray(), false));
543
544 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
545 assertTrue(offerPacket instanceof DhcpOfferPacket);
546 DhcpResults dhcpResults = offerPacket.toDhcpResults();
547 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 +0900548 "domain123.co.uk", "192.0.2.254", null, 49094, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900549 }
550
551 @SmallTest
552 public void testBug2136() throws Exception {
553 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
554 // Ethernet header.
555 "bcf5ac000000d0c7890000000800" +
556 // IP header.
557 "4500014c00000000ff119beac3eaf3880a3f5d04" +
558 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
559 "0043004401387574" +
560 // BOOTP header.
561 "0201060163339a3000050000000000000a209ecd0000000000000000" +
562 // MAC address.
563 "bcf5ac00000000000000000000000000" +
564 // Server name.
565 "0000000000000000000000000000000000000000000000000000000000000000" +
566 "0000000000000000000000000000000000000000000000000000000000000000" +
567 // File.
568 "0000000000000000000000000000000000000000000000000000000000000000" +
569 "0000000000000000000000000000000000000000000000000000000000000000" +
570 "0000000000000000000000000000000000000000000000000000000000000000" +
571 "0000000000000000000000000000000000000000000000000000000000000000" +
572 // Options.
573 "6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
574 "0f0b6c616e63732e61632e756b000000000000000000ff00000000"
575 ).toCharArray(), false));
576
577 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
578 assertTrue(offerPacket instanceof DhcpOfferPacket);
579 assertEquals("BCF5AC000000", HexDump.toHexString(offerPacket.getClientMac()));
580 DhcpResults dhcpResults = offerPacket.toDhcpResults();
581 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 +0900582 "lancs.ac.uk", "10.32.255.128", null, 7200, false, 0, dhcpResults);
Lorenzo Colittif28e62b2015-07-27 16:35:22 +0900583 }
Erik Klineb19238c2015-10-06 18:43:17 +0900584
585 @SmallTest
586 public void testUdpServerAnySourcePort() throws Exception {
587 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
588 // Ethernet header.
589 "9cd917000000001c2e0000000800" +
590 // IP header.
591 "45a00148000040003d115087d18194fb0a0f7af2" +
592 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
593 // NOTE: The server source port is not the canonical port 67.
594 "C29F004401341268" +
595 // BOOTP header.
596 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
597 // MAC address.
598 "9cd91700000000000000000000000000" +
599 // Server name.
600 "0000000000000000000000000000000000000000000000000000000000000000" +
601 "0000000000000000000000000000000000000000000000000000000000000000" +
602 // File.
603 "0000000000000000000000000000000000000000000000000000000000000000" +
604 "0000000000000000000000000000000000000000000000000000000000000000" +
605 "0000000000000000000000000000000000000000000000000000000000000000" +
606 "0000000000000000000000000000000000000000000000000000000000000000" +
607 // Options.
608 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
609 "d18180060f0777766d2e6564751c040a0fffffff000000"
610 ).toCharArray(), false));
611
612 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
613 assertTrue(offerPacket instanceof DhcpOfferPacket);
614 assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac()));
615 DhcpResults dhcpResults = offerPacket.toDhcpResults();
616 assertDhcpResults("10.15.122.242/16", "10.15.200.23",
617 "209.129.128.3,209.129.148.3,209.129.128.6",
Lorenzo Colitti77fdf232016-03-31 16:20:22 +0900618 "wvm.edu", "10.1.105.252", null, 86400, false, 0, dhcpResults);
Erik Klineb19238c2015-10-06 18:43:17 +0900619 }
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900620
621 @SmallTest
Lorenzo Colittif55c9c12016-03-02 13:31:52 +0900622 public void testUdpInvalidDstPort() throws Exception {
623 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
624 // Ethernet header.
625 "9cd917000000001c2e0000000800" +
626 // IP header.
627 "45a00148000040003d115087d18194fb0a0f7af2" +
628 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
629 // NOTE: The destination port is a non-DHCP port.
630 "0043aaaa01341268" +
631 // BOOTP header.
632 "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
633 // MAC address.
634 "9cd91700000000000000000000000000" +
635 // Server name.
636 "0000000000000000000000000000000000000000000000000000000000000000" +
637 "0000000000000000000000000000000000000000000000000000000000000000" +
638 // File.
639 "0000000000000000000000000000000000000000000000000000000000000000" +
640 "0000000000000000000000000000000000000000000000000000000000000000" +
641 "0000000000000000000000000000000000000000000000000000000000000000" +
642 "0000000000000000000000000000000000000000000000000000000000000000" +
643 // Options.
644 "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
645 "d18180060f0777766d2e6564751c040a0fffffff000000"
646 ).toCharArray(), false));
647
648 try {
649 DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
650 fail("Packet with invalid dst port did not throw ParseException");
651 } catch (ParseException expected) {}
652 }
653
654 @SmallTest
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900655 public void testMultipleRouters() throws Exception {
656 final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
657 // Ethernet header.
658 "fc3d93000000" + "081735000000" + "0800" +
659 // IP header.
660 "45000148c2370000ff117ac2c0a8bd02ffffffff" +
661 // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
662 "0043004401343beb" +
663 // BOOTP header.
664 "0201060027f518e20000800000000000c0a8bd310000000000000000" +
665 // MAC address.
666 "fc3d9300000000000000000000000000" +
667 // Server name.
668 "0000000000000000000000000000000000000000000000000000000000000000" +
669 "0000000000000000000000000000000000000000000000000000000000000000" +
670 // File.
671 "0000000000000000000000000000000000000000000000000000000000000000" +
672 "0000000000000000000000000000000000000000000000000000000000000000" +
673 "0000000000000000000000000000000000000000000000000000000000000000" +
674 "0000000000000000000000000000000000000000000000000000000000000000" +
675 // Options.
676 "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
677 "0308c0a8bd01ffffff0006080808080808080404ff000000000000"
678 ).toCharArray(), false));
679
680 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
681 assertTrue(offerPacket instanceof DhcpOfferPacket);
682 assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac()));
683 DhcpResults dhcpResults = offerPacket.toDhcpResults();
684 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 +0900685 null, "192.171.189.2", null, 28800, false, 0, dhcpResults);
Lorenzo Colitti025f4a52015-09-17 07:55:29 +0900686 }
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900687
688 @SmallTest
689 public void testDiscoverPacket() throws Exception {
690 short secs = 7;
691 int transactionId = 0xdeadbeef;
692 byte[] hwaddr = {
693 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a
694 };
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900695
696 ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
697 DhcpPacket.ENCAP_L2, transactionId, secs, hwaddr,
Erik Kline1d511ab2016-03-24 17:30:13 +0900698 false /* do unicast */, DhcpClient.REQUESTED_PARAMS);
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900699
700 byte[] headers = new byte[] {
701 // Ethernet header.
702 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
703 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
704 (byte) 0x08, (byte) 0x00,
705 // IP header.
Erik Kline1d511ab2016-03-24 17:30:13 +0900706 (byte) 0x45, (byte) 0x10, (byte) 0x01, (byte) 0x56,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900707 (byte) 0x00, (byte) 0x00, (byte) 0x40, (byte) 0x00,
Erik Kline1d511ab2016-03-24 17:30:13 +0900708 (byte) 0x40, (byte) 0x11, (byte) 0x39, (byte) 0x88,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900709 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
710 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff,
711 // UDP header.
712 (byte) 0x00, (byte) 0x44, (byte) 0x00, (byte) 0x43,
Erik Kline1d511ab2016-03-24 17:30:13 +0900713 (byte) 0x01, (byte) 0x42, (byte) 0x6a, (byte) 0x4a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900714 // BOOTP.
715 (byte) 0x01, (byte) 0x01, (byte) 0x06, (byte) 0x00,
716 (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
717 (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00,
718 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
719 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
720 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
721 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
722 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b,
723 (byte) 0xb1, (byte) 0x7a
724 };
725 byte[] options = new byte[] {
726 // Magic cookie 0x63825363.
727 (byte) 0x63, (byte) 0x82, (byte) 0x53, (byte) 0x63,
728 // Message type DISCOVER.
729 (byte) 0x35, (byte) 0x01, (byte) 0x01,
730 // Client identifier Ethernet, da:01:19:5b:b1:7a.
731 (byte) 0x3d, (byte) 0x07,
732 (byte) 0x01,
733 (byte) 0xda, (byte) 0x01, (byte) 0x19, (byte) 0x5b, (byte) 0xb1, (byte) 0x7a,
734 // Max message size 1500.
735 (byte) 0x39, (byte) 0x02, (byte) 0x05, (byte) 0xdc,
736 // Version "android-dhcp-???".
737 (byte) 0x3c, (byte) 0x10,
738 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-', 'd', 'h', 'c', 'p', '-', '?', '?', '?',
739 // Hostname "android-01234567890abcde"
740 (byte) 0x0c, (byte) 0x18,
741 'a', 'n', 'd', 'r', 'o', 'i', 'd', '-',
742 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e',
743 // Requested parameter list.
Erik Kline1d511ab2016-03-24 17:30:13 +0900744 (byte) 0x37, (byte) 0x0a,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900745 DHCP_SUBNET_MASK,
746 DHCP_ROUTER,
747 DHCP_DNS_SERVER,
748 DHCP_DOMAIN_NAME,
749 DHCP_MTU,
Erik Kline1d511ab2016-03-24 17:30:13 +0900750 DHCP_BROADCAST_ADDRESS,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900751 DHCP_LEASE_TIME,
Erik Kline1d511ab2016-03-24 17:30:13 +0900752 DHCP_RENEWAL_TIME,
753 DHCP_REBINDING_TIME,
754 DHCP_VENDOR_INFO,
Lorenzo Colitti6c7acb62015-06-29 17:10:45 +0900755 // End options.
756 (byte) 0xff,
757 // Our packets are always of even length. TODO: find out why and possibly fix it.
758 (byte) 0x00
759 };
760 byte[] expected = new byte[DhcpPacket.MIN_PACKET_LENGTH_L2 + options.length];
761 assertTrue((expected.length & 1) == 0);
762 System.arraycopy(headers, 0, expected, 0, headers.length);
763 System.arraycopy(options, 0, expected, DhcpPacket.MIN_PACKET_LENGTH_L2, options.length);
764
765 byte[] actual = new byte[packet.limit()];
766 packet.get(actual);
767 String msg =
768 "Expected:\n " + Arrays.toString(expected) +
769 "\nActual:\n " + Arrays.toString(actual);
770 assertTrue(msg, Arrays.equals(expected, actual));
771 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900772}