blob: e0e3fcf61c50b4a83e7d06c763c6ac2ee50ba09e [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;
24import junit.framework.TestCase;
25
26import java.net.Inet4Address;
27import java.nio.ByteBuffer;
28
29import static android.net.dhcp.DhcpPacket.*;
30
31
32public class DhcpPacketTest extends TestCase {
33
34 private static Inet4Address SERVER_ADDR =
35 (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
36 private static Inet4Address CLIENT_ADDR =
37 (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
Lorenzo Colittif68edb12015-06-02 17:46:24 +090038 // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
39 // doesn't use == instead of equals when comparing addresses.
40 private static Inet4Address ANY = (Inet4Address) NetworkUtils.numericToInetAddress("0.0.0.0");
41
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090042 private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
43
44 class TestDhcpPacket extends DhcpPacket {
45 private byte mType;
46 // TODO: Make this a map of option numbers to bytes instead.
Lorenzo Colittif68edb12015-06-02 17:46:24 +090047 private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090048
Lorenzo Colittif68edb12015-06-02 17:46:24 +090049 public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
50 super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
Lorenzo Colitti3e979322015-04-21 15:22:17 +090051 CLIENT_MAC, true);
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090052 mType = type;
Lorenzo Colittid9735372015-06-02 13:15:50 +090053 }
54
Lorenzo Colittif68edb12015-06-02 17:46:24 +090055 public TestDhcpPacket(byte type) {
56 this(type, INADDR_ANY, CLIENT_ADDR);
57 }
58
Lorenzo Colittid9735372015-06-02 13:15:50 +090059 public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090060 mDomainBytes = domainBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090061 return this;
62 }
63
64 public TestDhcpPacket setVendorInfoBytes(byte[] vendorInfoBytes) {
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090065 mVendorInfoBytes = vendorInfoBytes;
Lorenzo Colittid9735372015-06-02 13:15:50 +090066 return this;
67 }
68
69 public TestDhcpPacket setLeaseTimeBytes(byte[] leaseTimeBytes) {
70 mLeaseTimeBytes = leaseTimeBytes;
71 return this;
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090072 }
73
Lorenzo Colittif68edb12015-06-02 17:46:24 +090074 public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
75 mNetmaskBytes = netmaskBytes;
76 return this;
77 }
78
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +090079 public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
80 ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
81 fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
82 DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
83 return result;
84 }
85
86 public void finishPacket(ByteBuffer buffer) {
87 addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
88 if (mDomainBytes != null) {
89 addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
90 }
91 if (mVendorInfoBytes != null) {
92 addTlv(buffer, DHCP_VENDOR_CLASS_ID, mVendorInfoBytes);
93 }
Lorenzo Colittid9735372015-06-02 13:15:50 +090094 if (mLeaseTimeBytes != null) {
95 addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
96 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +090097 if (mNetmaskBytes != null) {
98 addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
99 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900100 addTlvEnd(buffer);
101 }
102
103 // Convenience method.
104 public ByteBuffer build() {
105 // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
106 ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
107 pkt.flip();
108 return pkt;
109 }
110 }
111
112 private void assertDomainAndVendorInfoParses(
113 String expectedDomain, byte[] domainBytes,
114 String expectedVendorInfo, byte[] vendorInfoBytes) {
Lorenzo Colittid9735372015-06-02 13:15:50 +0900115 ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER)
116 .setDomainBytes(domainBytes)
117 .setVendorInfoBytes(vendorInfoBytes)
118 .build();
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900119 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
120 assertEquals(expectedDomain, offerPacket.mDomainName);
121 assertEquals(expectedVendorInfo, offerPacket.mVendorId);
122 }
123
124 @SmallTest
125 public void testDomainName() throws Exception {
126 byte[] nullByte = new byte[] { 0x00 };
127 byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
128 byte[] nonNullDomain = new byte[] {
129 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
130 };
131 byte[] trailingNullDomain = new byte[] {
132 (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
133 };
134 byte[] embeddedNullsDomain = new byte[] {
135 (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
136 };
137 byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
138
139 byte[] meteredEmbeddedNull = metered.clone();
140 meteredEmbeddedNull[7] = (char) 0;
141
142 byte[] meteredTrailingNull = metered.clone();
143 meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
144
145 assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
146 assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
147 assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
148 assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
149 "ANDROID\u0000METERED", meteredEmbeddedNull);
150 assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
151 "ANDROID_METERE\u0000", meteredTrailingNull);
152 }
Lorenzo Colittid9735372015-06-02 13:15:50 +0900153
154 private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime,
155 long leaseTimeMillis, byte[] leaseTimeBytes) {
156 TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER);
157 if (leaseTimeBytes != null) {
158 testPacket.setLeaseTimeBytes(leaseTimeBytes);
159 }
160 ByteBuffer packet = testPacket.build();
161 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
162 if (!expectValid) {
163 assertNull(offerPacket);
164 return;
165 }
166 assertEquals(rawLeaseTime, offerPacket.mLeaseTime);
167 DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash.
168 assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis());
169 }
170
171 @SmallTest
172 public void testLeaseTime() throws Exception {
173 byte[] noLease = null;
174 byte[] tooShortLease = new byte[] { 0x00, 0x00 };
175 byte[] tooLongLease = new byte[] { 0x00, 0x00, 0x00, 60, 0x01 };
176 byte[] zeroLease = new byte[] { 0x00, 0x00, 0x00, 0x00 };
177 byte[] tenSecondLease = new byte[] { 0x00, 0x00, 0x00, 10 };
178 byte[] oneMinuteLease = new byte[] { 0x00, 0x00, 0x00, 60 };
179 byte[] fiveMinuteLease = new byte[] { 0x00, 0x00, 0x01, 0x2c };
180 byte[] oneDayLease = new byte[] { 0x00, 0x01, 0x51, (byte) 0x80 };
181 byte[] maxIntPlusOneLease = new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 };
182 byte[] infiniteLease = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
183
184 assertLeaseTimeParses(true, null, 0, noLease);
185 assertLeaseTimeParses(false, null, 0, tooShortLease);
186 assertLeaseTimeParses(false, null, 0, tooLongLease);
187 assertLeaseTimeParses(true, 0, 60 * 1000, zeroLease);
188 assertLeaseTimeParses(true, 10, 60 * 1000, tenSecondLease);
189 assertLeaseTimeParses(true, 60, 60 * 1000, oneMinuteLease);
190 assertLeaseTimeParses(true, 300, 300 * 1000, fiveMinuteLease);
191 assertLeaseTimeParses(true, 86400, 86400 * 1000, oneDayLease);
192 assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
193 assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
194 }
Lorenzo Colittif68edb12015-06-02 17:46:24 +0900195
196 private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
197 byte[] netmaskBytes) {
198 checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
199 checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
200 }
201
202 private void checkIpAddress(String expected, byte type,
203 Inet4Address clientIp, Inet4Address yourIp,
204 byte[] netmaskBytes) {
205 ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
206 .setNetmaskBytes(netmaskBytes)
207 .build();
208 DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
209 DhcpResults results = offerPacket.toDhcpResults();
210
211 if (expected != null) {
212 LinkAddress expectedAddress = new LinkAddress(expected);
213 assertEquals(expectedAddress, results.ipAddress);
214 } else {
215 assertNull(results);
216 }
217 }
218
219 @SmallTest
220 public void testIpAddress() throws Exception {
221 byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
222 byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
223 byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
224 Inet4Address example1 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
225 Inet4Address example2 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.43");
226
227 // A packet without any addresses is not valid.
228 checkIpAddress(null, ANY, ANY, slash24Netmask);
229
230 // ClientIP is used iff YourIP is not present.
231 checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
232 checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
233 checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
234
235 // Invalid netmasks are ignored.
236 checkIpAddress(null, example2, ANY, invalidNetmask);
237
238 // If there is no netmask, implicit netmasks are used.
239 checkIpAddress("192.0.2.43/24", ANY, example2, null);
240 }
Lorenzo Colittic2abb2b2015-03-31 16:26:57 +0900241}