• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.dhcp;
18 
19 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ALL;
20 import static com.android.net.module.util.NetworkStackConstants.IPV4_ADDR_ANY;
21 
22 import android.net.DhcpResults;
23 import android.net.LinkAddress;
24 import android.net.metrics.DhcpErrorEvent;
25 import android.net.networkstack.aidl.dhcp.DhcpOption;
26 import android.os.Build;
27 import android.os.SystemProperties;
28 import android.system.OsConstants;
29 import android.text.TextUtils;
30 
31 import androidx.annotation.NonNull;
32 import androidx.annotation.Nullable;
33 import androidx.annotation.VisibleForTesting;
34 
35 import com.android.net.module.util.Inet4AddressUtils;
36 import com.android.networkstack.apishim.common.ShimUtils;
37 
38 import java.io.UnsupportedEncodingException;
39 import java.net.Inet4Address;
40 import java.net.UnknownHostException;
41 import java.nio.BufferUnderflowException;
42 import java.nio.ByteBuffer;
43 import java.nio.ByteOrder;
44 import java.nio.ShortBuffer;
45 import java.nio.charset.StandardCharsets;
46 import java.util.ArrayList;
47 import java.util.Arrays;
48 import java.util.List;
49 
50 /**
51  * Defines basic data and operations needed to build and use packets for the
52  * DHCP protocol.  Subclasses create the specific packets used at each
53  * stage of the negotiation.
54  *
55  * @hide
56  */
57 public abstract class DhcpPacket {
58     protected static final String TAG = "DhcpPacket";
59 
60     // TODO: use NetworkStackConstants.IPV4_MIN_MTU once this class is moved to the network stack.
61     private static final int IPV4_MIN_MTU = 68;
62 
63     // dhcpcd has a minimum lease of 20 seconds, but DhcpStateMachine would refuse to wake up the
64     // CPU for anything shorter than 5 minutes. For sanity's sake, this must be higher than the
65     // DHCP client timeout.
66     public static final int MINIMUM_LEASE = 60;
67     public static final int INFINITE_LEASE = (int) 0xffffffff;
68 
69     public static final Inet4Address INADDR_ANY = IPV4_ADDR_ANY;
70     public static final Inet4Address INADDR_BROADCAST = IPV4_ADDR_ALL;
71     public static final byte[] ETHER_BROADCAST = new byte[] {
72             (byte) 0xff, (byte) 0xff, (byte) 0xff,
73             (byte) 0xff, (byte) 0xff, (byte) 0xff,
74     };
75 
76     /**
77      * Packet encapsulations.
78      */
79     public static final int ENCAP_L2 = 0;    // EthernetII header included
80     public static final int ENCAP_L3 = 1;    // IP/UDP header included
81     public static final int ENCAP_BOOTP = 2; // BOOTP contents only
82 
83     /**
84      * Minimum length of a DHCP packet, excluding options, in the above encapsulations.
85      */
86     public static final int MIN_PACKET_LENGTH_BOOTP = 236;  // See diagram in RFC 2131, section 2.
87     public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
88     public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
89 
90     public static final int HWADDR_LEN = 16;
91     public static final int MAX_OPTION_LEN = 255;
92 
93     // The lower boundary for V6ONLY_WAIT.
94     public static final long MIN_V6ONLY_WAIT_MS = 300_000;
95     public static final long V6ONLY_PREFERRED_ABSENCE = -1L;
96 
97     /**
98      * The minimum and maximum MTU that we are prepared to use. We set the minimum to the minimum
99      * IPv6 MTU because the IPv6 stack enters unusual codepaths when the link MTU drops below 1280,
100      * and does not recover if the MTU is brought above 1280 again. We set the maximum to 1500
101      * because in general it is risky to assume that the hardware is able to send/receive packets
102      * larger than 1500 bytes even if the network supports it.
103      */
104     private static final int MIN_MTU = 1280;
105     private static final int MAX_MTU = 1500;
106 
107     /**
108      * IP layer definitions.
109      */
110     private static final byte IP_TYPE_UDP = (byte) 0x11;
111 
112     /**
113      * IP: Version 4, Header Length 20 bytes
114      */
115     private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
116 
117     /**
118      * IP: Flags 0, Fragment Offset 0, Don't Fragment
119      */
120     private static final short IP_FLAGS_OFFSET = (short) 0x4000;
121 
122     /**
123      * IP: TOS
124      */
125     private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
126 
127     /**
128      * IP: TTL -- use default 64 from RFC1340
129      */
130     private static final byte IP_TTL = (byte) 0x40;
131 
132     /**
133      * The client DHCP port.
134      */
135     public static final short DHCP_CLIENT = (short) 68;
136 
137     /**
138      * The server DHCP port.
139      */
140     public static final short DHCP_SERVER = (short) 67;
141 
142     /**
143      * The message op code indicating a request from a client.
144      */
145     public static final byte DHCP_BOOTREQUEST = (byte) 1;
146 
147     /**
148      * The message op code indicating a response from the server.
149      */
150     public static final byte DHCP_BOOTREPLY = (byte) 2;
151 
152     /**
153      * The code type used to identify an Ethernet MAC address in the
154      * Client-ID field.
155      */
156     protected static final byte CLIENT_ID_ETHER = (byte) 1;
157 
158     /**
159      * The maximum length of a packet that can be constructed.
160      */
161     protected static final int MAX_LENGTH = 1500;
162 
163     /**
164      * The magic cookie that identifies this as a DHCP packet instead of BOOTP.
165      */
166     public static final int DHCP_MAGIC_COOKIE = 0x63825363;
167 
168     /**
169      * DHCP Optional Type: DHCP Subnet Mask
170      */
171     public static final byte DHCP_SUBNET_MASK = 1;
172     protected Inet4Address mSubnetMask;
173 
174     /**
175      * DHCP Optional Type: DHCP Router
176      */
177     public static final byte DHCP_ROUTER = 3;
178     protected List <Inet4Address> mGateways;
179 
180     /**
181      * DHCP Optional Type: DHCP DNS Server
182      */
183     public static final byte DHCP_DNS_SERVER = 6;
184     protected List<Inet4Address> mDnsServers;
185 
186     /**
187      * DHCP Optional Type: DHCP Host Name
188      */
189     public static final byte DHCP_HOST_NAME = 12;
190     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
191     public String mHostName;
192 
193     /**
194      * DHCP Optional Type: DHCP DOMAIN NAME
195      */
196     public static final byte DHCP_DOMAIN_NAME = 15;
197     protected String mDomainName;
198 
199     /**
200      * DHCP Optional Type: DHCP Interface MTU
201      */
202     public static final byte DHCP_MTU = 26;
203     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
204     public Short mMtu;
205 
206     /**
207      * DHCP Optional Type: DHCP BROADCAST ADDRESS
208      */
209     public static final byte DHCP_BROADCAST_ADDRESS = 28;
210     protected Inet4Address mBroadcastAddress;
211 
212     /**
213      * DHCP Optional Type: Vendor specific information
214      */
215     public static final byte DHCP_VENDOR_INFO = 43;
216     protected String mVendorInfo;
217 
218     /**
219      * Value of the vendor specific option used to indicate that the network is metered
220      */
221     public static final String VENDOR_INFO_ANDROID_METERED = "ANDROID_METERED";
222 
223     /**
224      * DHCP Optional Type: Option overload option
225      */
226     public static final byte DHCP_OPTION_OVERLOAD = 52;
227 
228     /**
229      * Possible values of the option overload option.
230      */
231     private static final byte OPTION_OVERLOAD_FILE = 1;
232     private static final byte OPTION_OVERLOAD_SNAME = 2;
233     private static final byte OPTION_OVERLOAD_BOTH = 3;
234 
235     /**
236      * DHCP Optional Type: DHCP Requested IP Address
237      */
238     public static final byte DHCP_REQUESTED_IP = 50;
239     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
240     public Inet4Address mRequestedIp;
241 
242     /**
243      * DHCP Optional Type: DHCP Lease Time
244      */
245     public static final byte DHCP_LEASE_TIME = 51;
246     protected Integer mLeaseTime;
247 
248     /**
249      * DHCP Optional Type: DHCP Message Type
250      */
251     public static final byte DHCP_MESSAGE_TYPE = 53;
252     // the actual type values
253     public static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
254     public static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
255     public static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
256     public static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
257     public static final byte DHCP_MESSAGE_TYPE_ACK = 5;
258     public static final byte DHCP_MESSAGE_TYPE_NAK = 6;
259     public static final byte DHCP_MESSAGE_TYPE_RELEASE = 7;
260     public static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
261 
262     /**
263      * DHCP Optional Type: DHCP Server Identifier
264      */
265     public static final byte DHCP_SERVER_IDENTIFIER = 54;
266     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
267     public Inet4Address mServerIdentifier;
268 
269     /**
270      * DHCP Optional Type: DHCP Parameter List
271      */
272     public static final byte DHCP_PARAMETER_LIST = 55;
273     protected byte[] mRequestedParams;
274 
275     /**
276      * DHCP Optional Type: DHCP MESSAGE
277      */
278     public static final byte DHCP_MESSAGE = 56;
279     protected String mMessage;
280 
281     /**
282      * DHCP Optional Type: Maximum DHCP Message Size
283      */
284     public static final byte DHCP_MAX_MESSAGE_SIZE = 57;
285     protected Short mMaxMessageSize;
286 
287     /**
288      * DHCP Optional Type: DHCP Renewal Time Value
289      */
290     public static final byte DHCP_RENEWAL_TIME = 58;
291     protected Integer mT1;
292 
293     /**
294      * DHCP Optional Type: Rebinding Time Value
295      */
296     public static final byte DHCP_REBINDING_TIME = 59;
297     protected Integer mT2;
298 
299     /**
300      * DHCP Optional Type: Vendor Class Identifier
301      */
302     public static final byte DHCP_VENDOR_CLASS_ID = 60;
303     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
304     public String mVendorId;
305 
306     /**
307      * DHCP Optional Type: DHCP Client Identifier
308      */
309     public static final byte DHCP_CLIENT_IDENTIFIER = 61;
310     protected byte[] mClientId;
311 
312     /**
313      * DHCP Optional Type: DHCP User Class option
314      */
315     public static final byte DHCP_USER_CLASS = 77;
316     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
317     public byte[] mUserClass;
318 
319     /**
320      * DHCP zero-length Optional Type: Rapid Commit. Per RFC4039, both DHCPDISCOVER and DHCPACK
321      * packet may include this option.
322      */
323     public static final byte DHCP_RAPID_COMMIT = 80;
324     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
325     public boolean mRapidCommit;
326 
327     /**
328      * DHCP IPv6-Only Preferred Option(RFC 8925).
329      * Indicate that a host supports an IPv6-only mode and willing to forgo obtaining an IPv4
330      * address for V6ONLY_WAIT period if the network provides IPv6 connectivity. V6ONLY_WAIT
331      * is 32-bit unsigned integer, so the Integer value cannot be used as-is.
332      */
333     public static final byte DHCP_IPV6_ONLY_PREFERRED = (byte) 108;
334     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
335     public Integer mIpv6OnlyWaitTime;
336 
337     public static final byte DHCP_CAPTIVE_PORTAL = (byte) 114;
338     protected String mCaptivePortalUrl;
339 
340     /**
341      * DHCP zero-length option code: pad
342      */
343     public static final byte DHCP_OPTION_PAD = 0x00;
344 
345     /**
346      * DHCP zero-length option code: end of options
347      */
348     public static final byte DHCP_OPTION_END = (byte) 0xff;
349 
350     /**
351      * The transaction identifier used in this particular DHCP negotiation
352      */
353     protected final int mTransId;
354 
355     /**
356      * The seconds field in the BOOTP header. Per RFC, should be nonzero in client requests only.
357      */
358     protected final short mSecs;
359 
360     /**
361      * The IP address of the client host.  This address is typically
362      * proposed by the client (from an earlier DHCP negotiation) or
363      * supplied by the server.
364      */
365     @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
366     public final Inet4Address mClientIp;
367     protected final Inet4Address mYourIp;
368     private final Inet4Address mNextIp;
369     protected final Inet4Address mRelayIp;
370 
371     /**
372      * Does the client request a broadcast response?
373      */
374     protected boolean mBroadcast;
375 
376     /**
377      * The six-octet MAC of the client.
378      */
379     protected final byte[] mClientMac;
380 
381     /**
382      * The server host name from server.
383      */
384     protected String mServerHostName;
385 
386     /**
387      * The customized DHCP client options to be sent.
388      */
389     @Nullable
390     protected List<DhcpOption> mCustomizedClientOptions;
391 
392     /**
393      * Asks the packet object to create a ByteBuffer serialization of
394      * the packet for transmission.
395      */
buildPacket(int encap, short destUdp, short srcUdp)396     public abstract ByteBuffer buildPacket(int encap, short destUdp,
397         short srcUdp);
398 
399     /**
400      * Allows the concrete class to fill in packet-type-specific details,
401      * typically optional parameters at the end of the packet.
402      */
finishPacket(ByteBuffer buffer)403     abstract void finishPacket(ByteBuffer buffer);
404 
405     // Set in unit tests, to ensure that the test does not break when run on different devices and
406     // on different releases.
407     static String sTestOverrideVendorId = null;
408 
DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp, Inet4Address nextIp, Inet4Address relayIp, byte[] clientMac, boolean broadcast)409     protected DhcpPacket(int transId, short secs, Inet4Address clientIp, Inet4Address yourIp,
410                          Inet4Address nextIp, Inet4Address relayIp,
411                          byte[] clientMac, boolean broadcast) {
412         mTransId = transId;
413         mSecs = secs;
414         mClientIp = clientIp;
415         mYourIp = yourIp;
416         mNextIp = nextIp;
417         mRelayIp = relayIp;
418         mClientMac = clientMac;
419         mBroadcast = broadcast;
420     }
421 
422     /**
423      * Returns the transaction ID.
424      */
getTransactionId()425     public int getTransactionId() {
426         return mTransId;
427     }
428 
429     /**
430      * Returns the client MAC.
431      */
getClientMac()432     public byte[] getClientMac() {
433         return mClientMac;
434     }
435 
436     // TODO: refactor DhcpClient to set clientId when constructing packets and remove
437     // hasExplicitClientId logic
438     /**
439      * Returns whether a client ID was set in the options for this packet.
440      */
hasExplicitClientId()441     public boolean hasExplicitClientId() {
442         return mClientId != null;
443     }
444 
445     /**
446      * Convenience method to return the client ID if it was set explicitly, or null otherwise.
447      */
448     @Nullable
getExplicitClientIdOrNull()449     public byte[] getExplicitClientIdOrNull() {
450         return hasExplicitClientId() ? getClientId() : null;
451     }
452 
453     /**
454      * Returns the client ID. If not set explicitly, this follows RFC 2132 and creates a client ID
455      * based on the hardware address.
456      */
getClientId()457     public byte[] getClientId() {
458         final byte[] clientId;
459         if (hasExplicitClientId()) {
460             clientId = Arrays.copyOf(mClientId, mClientId.length);
461         } else {
462             clientId = new byte[mClientMac.length + 1];
463             clientId[0] = CLIENT_ID_ETHER;
464             System.arraycopy(mClientMac, 0, clientId, 1, mClientMac.length);
465         }
466         return clientId;
467     }
468 
469     /**
470      * Returns whether a parameter is included in the parameter request list option of this packet.
471      *
472      * <p>If there is no parameter request list option in the packet, false is returned.
473      *
474      * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
475      */
hasRequestedParam(byte paramId)476     public boolean hasRequestedParam(byte paramId) {
477         if (mRequestedParams == null) {
478             return false;
479         }
480 
481         for (byte reqParam : mRequestedParams) {
482             if (reqParam == paramId) {
483                 return true;
484             }
485         }
486         return false;
487     }
488 
489     /**
490      * Creates a new L3 packet (including IP header) containing the
491      * DHCP udp packet.  This method relies upon the delegated method
492      * finishPacket() to insert the per-packet contents.
493      */
fillInPacket(int encap, Inet4Address destIp, Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf, byte requestCode, boolean broadcast)494     protected void fillInPacket(int encap, Inet4Address destIp,
495         Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf,
496         byte requestCode, boolean broadcast) {
497         byte[] destIpArray = destIp.getAddress();
498         byte[] srcIpArray = srcIp.getAddress();
499         int ipHeaderOffset = 0;
500         int ipLengthOffset = 0;
501         int ipChecksumOffset = 0;
502         int endIpHeader = 0;
503         int udpHeaderOffset = 0;
504         int udpLengthOffset = 0;
505         int udpChecksumOffset = 0;
506 
507         buf.clear();
508         buf.order(ByteOrder.BIG_ENDIAN);
509 
510         if (encap == ENCAP_L2) {
511             buf.put(ETHER_BROADCAST);
512             buf.put(mClientMac);
513             buf.putShort((short) OsConstants.ETH_P_IP);
514         }
515 
516         // if a full IP packet needs to be generated, put the IP & UDP
517         // headers in place, and pre-populate with artificial values
518         // needed to seed the IP checksum.
519         if (encap <= ENCAP_L3) {
520             ipHeaderOffset = buf.position();
521             buf.put(IP_VERSION_HEADER_LEN);
522             buf.put(IP_TOS_LOWDELAY);    // tos: IPTOS_LOWDELAY
523             ipLengthOffset = buf.position();
524             buf.putShort((short)0);  // length
525             buf.putShort((short)0);  // id
526             buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
527             buf.put(IP_TTL);    // TTL: use default 64 from RFC1340
528             buf.put(IP_TYPE_UDP);
529             ipChecksumOffset = buf.position();
530             buf.putShort((short) 0); // checksum
531 
532             buf.put(srcIpArray);
533             buf.put(destIpArray);
534             endIpHeader = buf.position();
535 
536             // UDP header
537             udpHeaderOffset = buf.position();
538             buf.putShort(srcUdp);
539             buf.putShort(destUdp);
540             udpLengthOffset = buf.position();
541             buf.putShort((short) 0); // length
542             udpChecksumOffset = buf.position();
543             buf.putShort((short) 0); // UDP checksum -- initially zero
544         }
545 
546         // DHCP payload
547         buf.put(requestCode);
548         buf.put((byte) 1); // Hardware Type: Ethernet
549         buf.put((byte) mClientMac.length); // Hardware Address Length
550         buf.put((byte) 0); // Hop Count
551         buf.putInt(mTransId);  // Transaction ID
552         buf.putShort(mSecs); // Elapsed Seconds
553 
554         if (broadcast) {
555             buf.putShort((short) 0x8000); // Flags
556         } else {
557             buf.putShort((short) 0x0000); // Flags
558         }
559 
560         buf.put(mClientIp.getAddress());
561         buf.put(mYourIp.getAddress());
562         buf.put(mNextIp.getAddress());
563         buf.put(mRelayIp.getAddress());
564         buf.put(mClientMac);
565         buf.position(buf.position() +
566                      (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes
567                      + 64     // empty server host name (64 bytes)
568                      + 128);  // empty boot file name (128 bytes)
569         buf.putInt(DHCP_MAGIC_COOKIE); // magic number
570         finishPacket(buf);
571 
572         // round up to an even number of octets
573         if ((buf.position() & 1) == 1) {
574             buf.put((byte) 0);
575         }
576 
577         // If an IP packet is being built, the IP & UDP checksums must be
578         // computed.
579         if (encap <= ENCAP_L3) {
580             // fix UDP header: insert length
581             short udpLen = (short)(buf.position() - udpHeaderOffset);
582             buf.putShort(udpLengthOffset, udpLen);
583             // fix UDP header: checksum
584             // checksum for UDP at udpChecksumOffset
585             int udpSeed = 0;
586 
587             // apply IPv4 pseudo-header.  Read IP address src and destination
588             // values from the IP header and accumulate checksum.
589             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
590             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
591             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
592             udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
593 
594             // accumulate extra data for the pseudo-header
595             udpSeed += IP_TYPE_UDP;
596             udpSeed += udpLen;
597             // and compute UDP checksum
598             buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
599                                                              udpHeaderOffset,
600                                                              buf.position()));
601             // fix IP header: insert length
602             buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset));
603             // fixup IP-header checksum
604             buf.putShort(ipChecksumOffset,
605                          (short) checksum(buf, 0, ipHeaderOffset, endIpHeader));
606         }
607     }
608 
609     /**
610      * Converts a signed short value to an unsigned int value.  Needed
611      * because Java does not have unsigned types.
612      */
intAbs(short v)613     private static int intAbs(short v) {
614         return v & 0xFFFF;
615     }
616 
617     /**
618      * Performs an IP checksum (used in IP header and across UDP
619      * payload) on the specified portion of a ByteBuffer.  The seed
620      * allows the checksum to commence with a specified value.
621      */
checksum(ByteBuffer buf, int seed, int start, int end)622     private int checksum(ByteBuffer buf, int seed, int start, int end) {
623         int sum = seed;
624         int bufPosition = buf.position();
625 
626         // set position of original ByteBuffer, so that the ShortBuffer
627         // will be correctly initialized
628         buf.position(start);
629         ShortBuffer shortBuf = buf.asShortBuffer();
630 
631         // re-set ByteBuffer position
632         buf.position(bufPosition);
633 
634         short[] shortArray = new short[(end - start) / 2];
635         shortBuf.get(shortArray);
636 
637         for (short s : shortArray) {
638             sum += intAbs(s);
639         }
640 
641         start += shortArray.length * 2;
642 
643         // see if a singleton byte remains
644         if (end != start) {
645             short b = buf.get(start);
646 
647             // make it unsigned
648             if (b < 0) {
649                 b += 256;
650             }
651 
652             sum += b * 256;
653         }
654 
655         sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
656         sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
657         int negated = ~sum;
658         return intAbs((short) negated);
659     }
660 
661     /**
662      * Adds an optional parameter containing a single byte value.
663      */
addTlv(ByteBuffer buf, byte type, byte value)664     protected static void addTlv(ByteBuffer buf, byte type, byte value) {
665         buf.put(type);
666         buf.put((byte) 1);
667         buf.put(value);
668     }
669 
670     /**
671      * Adds an optional parameter containing zero-length value.
672      */
addTlv(ByteBuffer buf, byte type)673     protected static void addTlv(ByteBuffer buf, byte type) {
674         buf.put(type);
675         buf.put((byte) 0);
676     }
677 
678     /**
679      * Adds an optional parameter containing an array of bytes.
680      *
681      * <p>This method is a no-op if the payload argument is null.
682      */
addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload)683     protected static void addTlv(ByteBuffer buf, byte type, @Nullable byte[] payload) {
684         if (payload != null) {
685             if (payload.length > MAX_OPTION_LEN) {
686                 throw new IllegalArgumentException("DHCP option too long: "
687                         + payload.length + " vs. " + MAX_OPTION_LEN);
688             }
689             buf.put(type);
690             buf.put((byte) payload.length);
691             buf.put(payload);
692         }
693     }
694 
695     /**
696      * Adds an optional parameter containing an IP address.
697      *
698      * <p>This method is a no-op if the address argument is null.
699      */
addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr)700     protected static void addTlv(ByteBuffer buf, byte type, @Nullable Inet4Address addr) {
701         if (addr != null) {
702             addTlv(buf, type, addr.getAddress());
703         }
704     }
705 
706     /**
707      * Adds an optional parameter containing a list of IP addresses.
708      *
709      * <p>This method is a no-op if the addresses argument is null or empty.
710      */
addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs)711     protected static void addTlv(ByteBuffer buf, byte type, @Nullable List<Inet4Address> addrs) {
712         if (addrs == null || addrs.size() == 0) return;
713 
714         int optionLen = 4 * addrs.size();
715         if (optionLen > MAX_OPTION_LEN) {
716             throw new IllegalArgumentException("DHCP option too long: "
717                     + optionLen + " vs. " + MAX_OPTION_LEN);
718         }
719 
720         buf.put(type);
721         buf.put((byte)(optionLen));
722 
723         for (Inet4Address addr : addrs) {
724             buf.put(addr.getAddress());
725         }
726     }
727 
728     /**
729      * Adds an optional parameter containing a short integer.
730      *
731      * <p>This method is a no-op if the value argument is null.
732      */
addTlv(ByteBuffer buf, byte type, @Nullable Short value)733     protected static void addTlv(ByteBuffer buf, byte type, @Nullable Short value) {
734         if (value != null) {
735             buf.put(type);
736             buf.put((byte) 2);
737             buf.putShort(value.shortValue());
738         }
739     }
740 
741     /**
742      * Adds an optional parameter containing a simple integer.
743      *
744      * <p>This method is a no-op if the value argument is null.
745      */
addTlv(ByteBuffer buf, byte type, @Nullable Integer value)746     protected static void addTlv(ByteBuffer buf, byte type, @Nullable Integer value) {
747         if (value != null) {
748             buf.put(type);
749             buf.put((byte) 4);
750             buf.putInt(value.intValue());
751         }
752     }
753 
754     /**
755      * Adds an optional parameter containing an ASCII string.
756      *
757      * <p>This method is a no-op if the string argument is null.
758      */
addTlv(ByteBuffer buf, byte type, @Nullable String str)759     protected static void addTlv(ByteBuffer buf, byte type, @Nullable String str) {
760         if (str != null) {
761             try {
762                 addTlv(buf, type, str.getBytes("US-ASCII"));
763             } catch (UnsupportedEncodingException e) {
764                 throw new IllegalArgumentException("String is not US-ASCII: " + str);
765             }
766         }
767     }
768 
769     /**
770      * Adds the special end-of-optional-parameters indicator.
771      */
addTlvEnd(ByteBuffer buf)772     protected static void addTlvEnd(ByteBuffer buf) {
773         buf.put((byte) 0xFF);
774     }
775 
776     /**
777      * Get the DHCP Vendor Class Identifier.
778      *
779      * By default the vendor Id is "android-dhcp-<version>". The default value will be overwritten
780      * with the customized option value if any.
781      */
getVendorId(@ullable List<DhcpOption> customizedClientOptions)782     private static String getVendorId(@Nullable List<DhcpOption> customizedClientOptions) {
783         if (sTestOverrideVendorId != null) return sTestOverrideVendorId;
784 
785         String vendorId = "android-dhcp-" + Build.VERSION.RELEASE;
786         if (customizedClientOptions != null) {
787             for (DhcpOption option : customizedClientOptions) {
788                 if (option.type == DHCP_VENDOR_CLASS_ID) {
789                     vendorId = readAsciiString(option.value, false);
790                     break;
791                 }
792             }
793         }
794         return vendorId;
795     }
796 
797     /**
798      * Get the DHCP client hostname after transliteration.
799      */
800     @VisibleForTesting
getHostname()801     public String getHostname() {
802         if (mHostName == null
803                 && !ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)) {
804             return SystemProperties.get("net.hostname");
805         }
806         return mHostName;
807     }
808 
809     /**
810      * Adds common client TLVs.
811      *
812      * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket
813      * methods to take them.
814      */
addCommonClientTlvs(ByteBuffer buf)815     protected void addCommonClientTlvs(ByteBuffer buf) {
816         addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
817         addTlv(buf, DHCP_VENDOR_CLASS_ID, mVendorId);
818         final String hn = getHostname();
819         if (!TextUtils.isEmpty(hn)) addTlv(buf, DHCP_HOST_NAME, hn);
820     }
821 
822     /**
823      * Adds OEM's customized client TLVs, which will be appended before the End Tlv.
824      */
addCustomizedClientTlvs(ByteBuffer buf)825     protected void addCustomizedClientTlvs(ByteBuffer buf) {
826         if (mCustomizedClientOptions == null) return;
827         for (DhcpOption option : mCustomizedClientOptions) {
828             // A null value means the option should only be put into the PRL.
829             if (option.value == null) continue;
830             // The vendor class ID was already added by addCommonClientTlvs.
831             if (option.type == DHCP_VENDOR_CLASS_ID) continue;
832             addTlv(buf, option.type, option.value);
833         }
834     }
835 
addCommonServerTlvs(ByteBuffer buf)836     protected void addCommonServerTlvs(ByteBuffer buf) {
837         addTlv(buf, DHCP_LEASE_TIME, mLeaseTime);
838         if (mLeaseTime != null && mLeaseTime != INFINITE_LEASE) {
839             // The client should renew at 1/2 the lease-expiry interval
840             addTlv(buf, DHCP_RENEWAL_TIME, (int) (Integer.toUnsignedLong(mLeaseTime) / 2));
841             // Default rebinding time is set as below by RFC2131
842             addTlv(buf, DHCP_REBINDING_TIME,
843                     (int) (Integer.toUnsignedLong(mLeaseTime) * 875L / 1000L));
844         }
845         addTlv(buf, DHCP_SUBNET_MASK, mSubnetMask);
846         addTlv(buf, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
847         addTlv(buf, DHCP_ROUTER, mGateways);
848         addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
849         addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
850         addTlv(buf, DHCP_HOST_NAME, mHostName);
851         addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
852         if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
853             addTlv(buf, DHCP_MTU, mMtu);
854         }
855         if (mIpv6OnlyWaitTime != null) {
856             addTlv(buf, DHCP_IPV6_ONLY_PREFERRED, (int) Integer.toUnsignedLong(mIpv6OnlyWaitTime));
857         }
858         addTlv(buf, DHCP_CAPTIVE_PORTAL, mCaptivePortalUrl);
859     }
860 
861     /**
862      * Converts a MAC from an array of octets to an ASCII string.
863      */
macToString(byte[] mac)864     public static String macToString(byte[] mac) {
865         String macAddr = "";
866 
867         for (int i = 0; i < mac.length; i++) {
868             String hexString = "0" + Integer.toHexString(mac[i]);
869 
870             // substring operation grabs the last 2 digits: this
871             // allows signed bytes to be converted correctly.
872             macAddr += hexString.substring(hexString.length() - 2);
873 
874             if (i != (mac.length - 1)) {
875                 macAddr += ":";
876             }
877         }
878 
879         return macAddr;
880     }
881 
toString()882     public String toString() {
883         String macAddr = macToString(mClientMac);
884 
885         return macAddr;
886     }
887 
888     /**
889      * Reads a four-octet value from a ByteBuffer and construct
890      * an IPv4 address from that value.
891      */
readIpAddress(ByteBuffer packet)892     private static Inet4Address readIpAddress(ByteBuffer packet) {
893         Inet4Address result = null;
894         byte[] ipAddr = new byte[4];
895         packet.get(ipAddr);
896 
897         try {
898             result = (Inet4Address) Inet4Address.getByAddress(ipAddr);
899         } catch (UnknownHostException ex) {
900             // ipAddr is numeric, so this should not be
901             // triggered.  However, if it is, just nullify
902             result = null;
903         }
904 
905         return result;
906     }
907 
908     /**
909      * Reads a string of specified length from the buffer.
910      */
readAsciiString(@onNull final ByteBuffer buf, int byteCount, boolean nullOk)911     private static String readAsciiString(@NonNull final ByteBuffer buf, int byteCount,
912             boolean nullOk) {
913         final byte[] bytes = new byte[byteCount];
914         buf.get(bytes);
915         return readAsciiString(bytes, nullOk);
916     }
917 
readAsciiString(@onNull final byte[] payload, boolean nullOk)918     private static String readAsciiString(@NonNull final byte[] payload, boolean nullOk) {
919         final byte[] bytes = payload;
920         int length = bytes.length;
921         if (!nullOk) {
922             // Stop at the first null byte. This is because some DHCP options (e.g., the domain
923             // name) are passed to netd via FrameworkListener, which refuses arguments containing
924             // null bytes. We don't do this by default because vendorInfo is an opaque string which
925             // could in theory contain null bytes.
926             for (length = 0; length < bytes.length; length++) {
927                 if (bytes[length] == 0) {
928                     break;
929                 }
930             }
931         }
932         return new String(bytes, 0, length, StandardCharsets.US_ASCII);
933     }
934 
isPacketToOrFromClient(short udpSrcPort, short udpDstPort)935     private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) {
936         return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT);
937     }
938 
isPacketServerToServer(short udpSrcPort, short udpDstPort)939     private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) {
940         return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER);
941     }
942 
943     public static class ParseException extends Exception {
944         public final int errorCode;
ParseException(int errorCode, String msg, Object... args)945         public ParseException(int errorCode, String msg, Object... args) {
946             super(String.format(msg, args));
947             this.errorCode = errorCode;
948         }
949     }
950 
skipOption(ByteBuffer packet, int optionLen)951     private static int skipOption(ByteBuffer packet, int optionLen)
952             throws BufferUnderflowException {
953         int expectedLen = 0;
954         for (int i = 0; i < optionLen; i++) {
955             expectedLen++;
956             packet.get();
957         }
958         return expectedLen;
959     }
960 
shouldSkipOption(byte optionType, byte[] optionsToSkip)961     private static boolean shouldSkipOption(byte optionType, byte[] optionsToSkip) {
962         for (byte option : optionsToSkip) {
963             if (option == optionType) return true;
964         }
965         return false;
966     }
967 
968     /**
969      * Creates a concrete DhcpPacket from the supplied ByteBuffer.  The
970      * buffer may have an L2 encapsulation (which is the full EthernetII
971      * format starting with the source-address MAC) or an L3 encapsulation
972      * (which starts with the IP header).
973      * <br>
974      * A subset of the optional parameters are parsed and are stored
975      * in object fields.
976      */
977     @VisibleForTesting
decodeFullPacket(ByteBuffer packet, int pktType, byte[] optionsToSkip)978     static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType, byte[] optionsToSkip)
979             throws ParseException {
980         // bootp parameters
981         int transactionId;
982         short secs;
983         Inet4Address clientIp;
984         Inet4Address yourIp;
985         Inet4Address nextIp;
986         Inet4Address relayIp;
987         byte[] clientMac;
988         byte[] clientId = null;
989         List<Inet4Address> dnsServers = new ArrayList<>();
990         List<Inet4Address> gateways = new ArrayList<>();  // aka router
991         Inet4Address serverIdentifier = null;
992         Inet4Address netMask = null;
993         String message = null;
994         String vendorId = null;
995         String vendorInfo = null;
996         boolean rapidCommit = false;
997         String captivePortalUrl = null;
998         byte[] expectedParams = null;
999         String hostName = null;
1000         String domainName = null;
1001         Inet4Address ipSrc = null;
1002         Inet4Address ipDst = null;
1003         Inet4Address bcAddr = null;
1004         Inet4Address requestedIp = null;
1005         String serverHostName;
1006         byte optionOverload = 0;
1007         byte[] userClass = null;
1008 
1009         // The following are all unsigned integers. Internally we store them as signed integers of
1010         // the same length because that way we're guaranteed that they can't be out of the range of
1011         // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need
1012         // to cast it.
1013         Short mtu = null;
1014         Short maxMessageSize = null;
1015         Integer leaseTime = null;
1016         Integer T1 = null;
1017         Integer T2 = null;
1018         Integer ipv6OnlyWaitTime = null;
1019 
1020         // dhcp options
1021         byte dhcpType = (byte) 0xFF;
1022 
1023         packet.order(ByteOrder.BIG_ENDIAN);
1024 
1025         // check to see if we need to parse L2, IP, and UDP encaps
1026         if (pktType == ENCAP_L2) {
1027             if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
1028                 throw new ParseException(DhcpErrorEvent.L2_TOO_SHORT,
1029                         "L2 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L2);
1030             }
1031 
1032             byte[] l2dst = new byte[6];
1033             byte[] l2src = new byte[6];
1034 
1035             packet.get(l2dst);
1036             packet.get(l2src);
1037 
1038             short l2type = packet.getShort();
1039 
1040             if (l2type != OsConstants.ETH_P_IP) {
1041                 throw new ParseException(DhcpErrorEvent.L2_WRONG_ETH_TYPE,
1042                         "Unexpected L2 type 0x%04x, expected 0x%04x", l2type, OsConstants.ETH_P_IP);
1043             }
1044         }
1045 
1046         if (pktType <= ENCAP_L3) {
1047             if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
1048                 throw new ParseException(DhcpErrorEvent.L3_TOO_SHORT,
1049                         "L3 packet too short, %d < %d", packet.remaining(), MIN_PACKET_LENGTH_L3);
1050             }
1051 
1052             byte ipTypeAndLength = packet.get();
1053             int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
1054             if (ipVersion != 4) {
1055                 throw new ParseException(
1056                         DhcpErrorEvent.L3_NOT_IPV4, "Invalid IP version %d", ipVersion);
1057             }
1058 
1059             // System.out.println("ipType is " + ipType);
1060             byte ipDiffServicesField = packet.get();
1061             short ipTotalLength = packet.getShort();
1062             short ipIdentification = packet.getShort();
1063             byte ipFlags = packet.get();
1064             byte ipFragOffset = packet.get();
1065             byte ipTTL = packet.get();
1066             byte ipProto = packet.get();
1067             short ipChksm = packet.getShort();
1068 
1069             ipSrc = readIpAddress(packet);
1070             ipDst = readIpAddress(packet);
1071 
1072             if (ipProto != IP_TYPE_UDP) {
1073                 throw new ParseException(
1074                         DhcpErrorEvent.L4_NOT_UDP, "Protocol not UDP: %d", ipProto);
1075             }
1076 
1077             // Skip options. This cannot cause us to read beyond the end of the buffer because the
1078             // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
1079             // MIN_PACKET_LENGTH_L3.
1080             int optionWords = ((ipTypeAndLength & 0x0f) - 5);
1081             for (int i = 0; i < optionWords; i++) {
1082                 packet.getInt();
1083             }
1084 
1085             // assume UDP
1086             short udpSrcPort = packet.getShort();
1087             short udpDstPort = packet.getShort();
1088             short udpLen = packet.getShort();
1089             short udpChkSum = packet.getShort();
1090 
1091             // Only accept packets to or from the well-known client port (expressly permitting
1092             // packets from ports other than the well-known server port; http://b/24687559), and
1093             // server-to-server packets, e.g. for relays.
1094             if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
1095                 !isPacketServerToServer(udpSrcPort, udpDstPort)) {
1096                 // This should almost never happen because we use SO_ATTACH_FILTER on the packet
1097                 // socket to drop packets that don't have the right source ports. However, it's
1098                 // possible that a packet arrives between when the socket is bound and when the
1099                 // filter is set. http://b/26696823 .
1100                 throw new ParseException(DhcpErrorEvent.L4_WRONG_PORT,
1101                         "Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
1102             }
1103         }
1104 
1105         // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
1106         if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
1107             throw new ParseException(DhcpErrorEvent.BOOTP_TOO_SHORT,
1108                         "Invalid type or BOOTP packet too short, %d < %d",
1109                         packet.remaining(), MIN_PACKET_LENGTH_BOOTP);
1110         }
1111 
1112         byte type = packet.get();
1113         byte hwType = packet.get();
1114         int addrLen = packet.get() & 0xff;
1115         byte hops = packet.get();
1116         transactionId = packet.getInt();
1117         secs = packet.getShort();
1118         short bootpFlags = packet.getShort();
1119         boolean broadcast = (bootpFlags & 0x8000) != 0;
1120         byte[] ipv4addr = new byte[4];
1121 
1122         try {
1123             packet.get(ipv4addr);
1124             clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
1125             packet.get(ipv4addr);
1126             yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
1127             packet.get(ipv4addr);
1128             nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
1129             packet.get(ipv4addr);
1130             relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
1131         } catch (UnknownHostException ex) {
1132             throw new ParseException(DhcpErrorEvent.L3_INVALID_IP,
1133                     "Invalid IPv4 address: %s", Arrays.toString(ipv4addr));
1134         }
1135 
1136         // Some DHCP servers have been known to announce invalid client hardware address values such
1137         // as 0xff. The legacy DHCP client accepted these becuause it does not check the length at
1138         // all but only checks that the interface MAC address matches the first bytes of the address
1139         // in the packets. We're a bit stricter: if the length is obviously invalid (i.e., bigger
1140         // than the size of the field), we fudge it to 6 (Ethernet). http://b/23725795
1141         // TODO: evaluate whether to make this test more liberal.
1142         if (addrLen > HWADDR_LEN) {
1143             addrLen = ETHER_BROADCAST.length;
1144         }
1145 
1146         clientMac = new byte[addrLen];
1147         packet.get(clientMac);
1148 
1149         // skip over address padding (16 octets allocated)
1150         packet.position(packet.position() + (16 - addrLen));
1151         serverHostName = readAsciiString(packet, 64, false);
1152         packet.position(packet.position() + 128);
1153 
1154         // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211
1155         if (packet.remaining() < 4) {
1156             throw new ParseException(DhcpErrorEvent.DHCP_NO_COOKIE, "not a DHCP message");
1157         }
1158 
1159         int dhcpMagicCookie = packet.getInt();
1160         if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
1161             throw new ParseException(DhcpErrorEvent.DHCP_BAD_MAGIC_COOKIE,
1162                     "Bad magic cookie 0x%08x, should be 0x%08x",
1163                     dhcpMagicCookie, DHCP_MAGIC_COOKIE);
1164         }
1165 
1166         // parse options
1167         boolean notFinishedOptions = true;
1168 
1169         while ((packet.position() < packet.limit()) && notFinishedOptions) {
1170             final byte optionType = packet.get(); // cannot underflow because position < limit
1171             try {
1172                 if (optionType == DHCP_OPTION_END) {
1173                     notFinishedOptions = false;
1174                 } else if (optionType == DHCP_OPTION_PAD) {
1175                     // The pad option doesn't have a length field. Nothing to do.
1176                 } else {
1177                     int optionLen = packet.get() & 0xFF;
1178                     int expectedLen = 0;
1179 
1180                     if (shouldSkipOption(optionType, optionsToSkip)) {
1181                         skipOption(packet, optionLen);
1182                         continue;
1183                     }
1184 
1185                     switch(optionType) {
1186                         case DHCP_SUBNET_MASK:
1187                             netMask = readIpAddress(packet);
1188                             expectedLen = 4;
1189                             break;
1190                         case DHCP_ROUTER:
1191                             for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
1192                                 gateways.add(readIpAddress(packet));
1193                             }
1194                             break;
1195                         case DHCP_DNS_SERVER:
1196                             for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
1197                                 dnsServers.add(readIpAddress(packet));
1198                             }
1199                             break;
1200                         case DHCP_HOST_NAME:
1201                             expectedLen = optionLen;
1202                             hostName = readAsciiString(packet, optionLen, false);
1203                             break;
1204                         case DHCP_MTU:
1205                             expectedLen = 2;
1206                             mtu = packet.getShort();
1207                             break;
1208                         case DHCP_DOMAIN_NAME:
1209                             expectedLen = optionLen;
1210                             domainName = readAsciiString(packet, optionLen, false);
1211                             break;
1212                         case DHCP_BROADCAST_ADDRESS:
1213                             bcAddr = readIpAddress(packet);
1214                             expectedLen = 4;
1215                             break;
1216                         case DHCP_REQUESTED_IP:
1217                             requestedIp = readIpAddress(packet);
1218                             expectedLen = 4;
1219                             break;
1220                         case DHCP_LEASE_TIME:
1221                             leaseTime = Integer.valueOf(packet.getInt());
1222                             expectedLen = 4;
1223                             break;
1224                         case DHCP_MESSAGE_TYPE:
1225                             dhcpType = packet.get();
1226                             expectedLen = 1;
1227                             break;
1228                         case DHCP_SERVER_IDENTIFIER:
1229                             serverIdentifier = readIpAddress(packet);
1230                             expectedLen = 4;
1231                             break;
1232                         case DHCP_PARAMETER_LIST:
1233                             expectedParams = new byte[optionLen];
1234                             packet.get(expectedParams);
1235                             expectedLen = optionLen;
1236                             break;
1237                         case DHCP_MESSAGE:
1238                             expectedLen = optionLen;
1239                             message = readAsciiString(packet, optionLen, false);
1240                             break;
1241                         case DHCP_MAX_MESSAGE_SIZE:
1242                             expectedLen = 2;
1243                             maxMessageSize = Short.valueOf(packet.getShort());
1244                             break;
1245                         case DHCP_RENEWAL_TIME:
1246                             expectedLen = 4;
1247                             T1 = Integer.valueOf(packet.getInt());
1248                             break;
1249                         case DHCP_REBINDING_TIME:
1250                             expectedLen = 4;
1251                             T2 = Integer.valueOf(packet.getInt());
1252                             break;
1253                         case DHCP_VENDOR_CLASS_ID:
1254                             expectedLen = optionLen;
1255                             // Embedded nulls are safe as this does not get passed to netd.
1256                             vendorId = readAsciiString(packet, optionLen, true);
1257                             break;
1258                         case DHCP_CLIENT_IDENTIFIER: { // Client identifier
1259                             clientId = new byte[optionLen];
1260                             packet.get(clientId);
1261                             expectedLen = optionLen;
1262                         } break;
1263                         case DHCP_VENDOR_INFO:
1264                             expectedLen = optionLen;
1265                             // Embedded nulls are safe as this does not get passed to netd.
1266                             vendorInfo = readAsciiString(packet, optionLen, true);
1267                             break;
1268                         case DHCP_OPTION_OVERLOAD:
1269                             expectedLen = 1;
1270                             optionOverload = packet.get();
1271                             optionOverload &= OPTION_OVERLOAD_BOTH;
1272                             break;
1273                         case DHCP_USER_CLASS:
1274                             userClass = new byte[optionLen];
1275                             packet.get(userClass);
1276                             expectedLen = optionLen;
1277                             break;
1278                         case DHCP_RAPID_COMMIT:
1279                             expectedLen = 0;
1280                             rapidCommit = true;
1281                             break;
1282                         case DHCP_CAPTIVE_PORTAL:
1283                             expectedLen = optionLen;
1284                             captivePortalUrl = readAsciiString(packet, optionLen, true);
1285                             break;
1286                         case DHCP_IPV6_ONLY_PREFERRED:
1287                             expectedLen = 4;
1288                             ipv6OnlyWaitTime = Integer.valueOf(packet.getInt());
1289                             break;
1290                         default:
1291                             expectedLen = skipOption(packet, optionLen);
1292                     }
1293 
1294                     if (expectedLen != optionLen) {
1295                         final int errorCode = DhcpErrorEvent.errorCodeWithOption(
1296                                 DhcpErrorEvent.DHCP_INVALID_OPTION_LENGTH, optionType);
1297                         throw new ParseException(errorCode,
1298                                 "Invalid length %d for option %d, expected %d",
1299                                 optionLen, optionType, expectedLen);
1300                     }
1301                 }
1302             } catch (BufferUnderflowException e) {
1303                 final int errorCode = DhcpErrorEvent.errorCodeWithOption(
1304                         DhcpErrorEvent.BUFFER_UNDERFLOW, optionType);
1305                 throw new ParseException(errorCode, "BufferUnderflowException");
1306             }
1307         }
1308 
1309         DhcpPacket newPacket;
1310 
1311         switch(dhcpType) {
1312             case (byte) 0xFF:
1313                 throw new ParseException(DhcpErrorEvent.DHCP_NO_MSG_TYPE,
1314                         "No DHCP message type option");
1315             case DHCP_MESSAGE_TYPE_DISCOVER:
1316                 newPacket = new DhcpDiscoverPacket(transactionId, secs, relayIp, clientMac,
1317                         broadcast, ipSrc, rapidCommit);
1318                 break;
1319             case DHCP_MESSAGE_TYPE_OFFER:
1320                 newPacket = new DhcpOfferPacket(
1321                     transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac);
1322                 break;
1323             case DHCP_MESSAGE_TYPE_REQUEST:
1324                 newPacket = new DhcpRequestPacket(
1325                     transactionId, secs, clientIp, relayIp, clientMac, broadcast);
1326                 break;
1327             case DHCP_MESSAGE_TYPE_DECLINE:
1328                 newPacket = new DhcpDeclinePacket(
1329                     transactionId, secs, clientIp, yourIp, nextIp, relayIp, clientMac, requestedIp,
1330                     serverIdentifier);
1331                 break;
1332             case DHCP_MESSAGE_TYPE_ACK:
1333                 newPacket = new DhcpAckPacket(
1334                     transactionId, secs, broadcast, ipSrc, relayIp, clientIp, yourIp, clientMac,
1335                     rapidCommit);
1336                 break;
1337             case DHCP_MESSAGE_TYPE_NAK:
1338                 newPacket = new DhcpNakPacket(
1339                         transactionId, secs, relayIp, clientMac, broadcast);
1340                 break;
1341             case DHCP_MESSAGE_TYPE_RELEASE:
1342                 if (serverIdentifier == null) {
1343                     throw new ParseException(DhcpErrorEvent.MISC_ERROR,
1344                             "DHCPRELEASE without server identifier");
1345                 }
1346                 newPacket = new DhcpReleasePacket(
1347                         transactionId, serverIdentifier, clientIp, relayIp, clientMac);
1348                 break;
1349             case DHCP_MESSAGE_TYPE_INFORM:
1350                 newPacket = new DhcpInformPacket(
1351                     transactionId, secs, clientIp, yourIp, nextIp, relayIp,
1352                     clientMac);
1353                 break;
1354             default:
1355                 throw new ParseException(DhcpErrorEvent.DHCP_UNKNOWN_MSG_TYPE,
1356                         "Unimplemented DHCP type %d", dhcpType);
1357         }
1358 
1359         newPacket.mBroadcastAddress = bcAddr;
1360         newPacket.mClientId = clientId;
1361         newPacket.mDnsServers = dnsServers;
1362         newPacket.mDomainName = domainName;
1363         newPacket.mGateways = gateways;
1364         newPacket.mHostName = hostName;
1365         newPacket.mLeaseTime = leaseTime;
1366         newPacket.mMessage = message;
1367         newPacket.mMtu = mtu;
1368         newPacket.mRequestedIp = requestedIp;
1369         newPacket.mRequestedParams = expectedParams;
1370         newPacket.mServerIdentifier = serverIdentifier;
1371         newPacket.mSubnetMask = netMask;
1372         newPacket.mMaxMessageSize = maxMessageSize;
1373         newPacket.mT1 = T1;
1374         newPacket.mT2 = T2;
1375         newPacket.mVendorId = vendorId;
1376         newPacket.mVendorInfo = vendorInfo;
1377         newPacket.mCaptivePortalUrl = captivePortalUrl;
1378         newPacket.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
1379         newPacket.mUserClass = userClass;
1380         if ((optionOverload & OPTION_OVERLOAD_SNAME) == 0) {
1381             newPacket.mServerHostName = serverHostName;
1382         } else {
1383             newPacket.mServerHostName = "";
1384         }
1385         return newPacket;
1386     }
1387 
1388     /**
1389      * Parse a packet from an array of bytes, stopping at the given length.
1390      */
decodeFullPacket(byte[] packet, int length, int pktType, byte[] optionsToSkip)1391     public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType,
1392             byte[] optionsToSkip) throws ParseException {
1393         ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
1394         try {
1395             return decodeFullPacket(buffer, pktType, optionsToSkip);
1396         } catch (ParseException e) {
1397             throw e;
1398         } catch (Exception e) {
1399             throw new ParseException(DhcpErrorEvent.PARSING_ERROR, e.getMessage());
1400         }
1401     }
1402 
1403     /**
1404      * Parse a packet from an array of bytes, stopping at the given length.
1405      */
decodeFullPacket(byte[] packet, int length, int pktType)1406     public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
1407             throws ParseException {
1408         return decodeFullPacket(packet, length, pktType, new byte[0]);
1409     }
1410 
1411     /**
1412      *  Construct a DhcpResults object from a DHCP reply packet.
1413      */
toDhcpResults()1414     public DhcpResults toDhcpResults() {
1415         Inet4Address ipAddress = mYourIp;
1416         if (ipAddress.equals(IPV4_ADDR_ANY)) {
1417             ipAddress = mClientIp;
1418             if (ipAddress.equals(IPV4_ADDR_ANY)) {
1419                 return null;
1420             }
1421         }
1422 
1423         int prefixLength;
1424         if (mSubnetMask != null) {
1425             try {
1426                 prefixLength = Inet4AddressUtils.netmaskToPrefixLength(mSubnetMask);
1427             } catch (IllegalArgumentException e) {
1428                 // Non-contiguous netmask.
1429                 return null;
1430             }
1431         } else {
1432             prefixLength = Inet4AddressUtils.getImplicitNetmask(ipAddress);
1433         }
1434 
1435         DhcpResults results = new DhcpResults();
1436         try {
1437             results.ipAddress = new LinkAddress(ipAddress, prefixLength);
1438         } catch (IllegalArgumentException e) {
1439             return null;
1440         }
1441 
1442         if (mGateways.size() > 0) {
1443             results.gateway = mGateways.get(0);
1444         }
1445 
1446         results.dnsServers.addAll(mDnsServers);
1447         results.domains = mDomainName;
1448         results.serverAddress = mServerIdentifier;
1449         results.vendorInfo = mVendorInfo;
1450         results.leaseDuration = (mLeaseTime != null) ? mLeaseTime : INFINITE_LEASE;
1451         results.mtu = (mMtu != null && MIN_MTU <= mMtu && mMtu <= MAX_MTU) ? mMtu : 0;
1452         results.serverHostName = mServerHostName;
1453         results.captivePortalApiUrl = mCaptivePortalUrl;
1454 
1455         return results;
1456     }
1457 
1458     /**
1459      * Returns the parsed lease time, in milliseconds, or 0 for infinite.
1460      */
getLeaseTimeMillis()1461     public long getLeaseTimeMillis() {
1462         // dhcpcd treats the lack of a lease time option as an infinite lease.
1463         if (mLeaseTime == null || mLeaseTime == INFINITE_LEASE) {
1464             return 0;
1465         } else if (0 <= mLeaseTime && mLeaseTime < MINIMUM_LEASE) {
1466             return MINIMUM_LEASE * 1000;
1467         } else {
1468             return (mLeaseTime & 0xffffffffL) * 1000;
1469         }
1470     }
1471 
1472     /**
1473      * Returns the IPv6-only wait time, in milliseconds, or -1 if the option is not present.
1474      */
getIpv6OnlyWaitTimeMillis()1475     public long getIpv6OnlyWaitTimeMillis() {
1476         if (mIpv6OnlyWaitTime == null) return V6ONLY_PREFERRED_ABSENCE;
1477         return Math.max(MIN_V6ONLY_WAIT_MS, Integer.toUnsignedLong(mIpv6OnlyWaitTime) * 1000);
1478     }
1479 
1480     /**
1481      * Builds a DHCP-DISCOVER packet from the required specified parameters.
1482      */
buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit, String hostname, List<DhcpOption> options)1483     public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
1484             short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams,
1485             boolean rapidCommit, String hostname, List<DhcpOption> options) {
1486         DhcpPacket pkt = new DhcpDiscoverPacket(transactionId, secs, INADDR_ANY /* relayIp */,
1487                 clientMac, broadcast, INADDR_ANY /* srcIp */, rapidCommit);
1488         pkt.mRequestedParams = expectedParams;
1489         pkt.mHostName = hostname;
1490         pkt.mCustomizedClientOptions = options;
1491         pkt.mVendorId = getVendorId(options);
1492         return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
1493     }
1494 
1495     /**
1496      * Builds a DHCP-DISCOVER packet from the required specified parameters.
1497      *
1498      * TODO: remove this method when automerger to mainline-prod is running.
1499      */
buildDiscoverPacket(int encap, int transactionId, short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams, boolean rapidCommit, String hostname)1500     public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
1501             short secs, byte[] clientMac, boolean broadcast, byte[] expectedParams,
1502             boolean rapidCommit, String hostname) {
1503         return buildDiscoverPacket(encap, transactionId, secs, clientMac, broadcast,
1504                 expectedParams, rapidCommit, hostname, new ArrayList<DhcpOption>());
1505     }
1506 
1507     /**
1508      * Builds a DHCP-OFFER packet from the required specified parameters.
1509      */
buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl, Integer ipv6OnlyWaitTime)1510     public static ByteBuffer buildOfferPacket(int encap, int transactionId,
1511             boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
1512             Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
1513             Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
1514             Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
1515             short mtu, String captivePortalUrl, Integer ipv6OnlyWaitTime) {
1516         DhcpPacket pkt = new DhcpOfferPacket(
1517                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
1518                 INADDR_ANY /* clientIp */, yourIp, mac);
1519         pkt.mGateways = gateways;
1520         pkt.mDnsServers = dnsServers;
1521         pkt.mLeaseTime = timeout;
1522         pkt.mDomainName = domainName;
1523         pkt.mHostName = hostname;
1524         pkt.mServerIdentifier = dhcpServerIdentifier;
1525         pkt.mSubnetMask = netMask;
1526         pkt.mBroadcastAddress = bcAddr;
1527         pkt.mMtu = mtu;
1528         pkt.mCaptivePortalUrl = captivePortalUrl;
1529         if (metered) {
1530             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
1531         }
1532         if (ipv6OnlyWaitTime != null) {
1533             pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
1534         }
1535         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
1536     }
1537 
1538     /**
1539      * Builds a DHCP-OFFER packet from the required specified parameters.
1540      */
buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, String captivePortalUrl)1541     public static ByteBuffer buildOfferPacket(int encap, int transactionId,
1542             boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
1543             Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
1544             Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
1545             Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
1546             short mtu, String captivePortalUrl) {
1547         return buildOfferPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp,
1548                 mac, timeout, netMask, bcAddr, gateways, dnsServers, dhcpServerIdentifier,
1549                 domainName, hostname, metered, mtu, captivePortalUrl, null /* V6ONLY_WAIT */);
1550     }
1551 
1552     /**
1553      * Builds a DHCP-ACK packet from the required specified parameters.
1554      */
buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl, Integer ipv6OnlyWaitTime)1555     public static ByteBuffer buildAckPacket(int encap, int transactionId,
1556             boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
1557             Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
1558             Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
1559             Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
1560             short mtu, boolean rapidCommit, String captivePortalUrl, Integer ipv6OnlyWaitTime) {
1561         DhcpPacket pkt = new DhcpAckPacket(
1562                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
1563                 mac, rapidCommit);
1564         pkt.mGateways = gateways;
1565         pkt.mDnsServers = dnsServers;
1566         pkt.mLeaseTime = timeout;
1567         pkt.mDomainName = domainName;
1568         pkt.mHostName = hostname;
1569         pkt.mSubnetMask = netMask;
1570         pkt.mServerIdentifier = dhcpServerIdentifier;
1571         pkt.mBroadcastAddress = bcAddr;
1572         pkt.mMtu = mtu;
1573         pkt.mCaptivePortalUrl = captivePortalUrl;
1574         if (metered) {
1575             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
1576         }
1577         if (ipv6OnlyWaitTime != null) {
1578             pkt.mIpv6OnlyWaitTime = ipv6OnlyWaitTime;
1579         }
1580         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
1581     }
1582 
1583     /**
1584      * Builds a DHCP-ACK packet from the required specified parameters.
1585      */
buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp, Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers, Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered, short mtu, boolean rapidCommit, String captivePortalUrl)1586     public static ByteBuffer buildAckPacket(int encap, int transactionId,
1587             boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
1588             Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
1589             Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
1590             Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
1591             short mtu, boolean rapidCommit, String captivePortalUrl) {
1592         return buildAckPacket(encap, transactionId, broadcast, serverIpAddr, relayIp, yourIp,
1593                 requestClientIp, mac, timeout, netMask, bcAddr, gateways, dnsServers,
1594                 dhcpServerIdentifier, domainName, hostname, metered, mtu, rapidCommit,
1595                 captivePortalUrl, null /* V6ONLY_WAIT */);
1596     }
1597 
1598     /**
1599      * Builds a DHCP-NAK packet from the required specified parameters.
1600      */
buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr, Inet4Address relayIp, byte[] mac, boolean broadcast, String message)1601     public static ByteBuffer buildNakPacket(int encap, int transactionId, Inet4Address serverIpAddr,
1602             Inet4Address relayIp, byte[] mac, boolean broadcast, String message) {
1603         DhcpPacket pkt = new DhcpNakPacket(
1604                 transactionId, (short) 0, relayIp, mac, broadcast);
1605         pkt.mMessage = message;
1606         pkt.mServerIdentifier = serverIpAddr;
1607         return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
1608     }
1609 
1610     /**
1611      * Builds a DHCP-REQUEST packet from the required specified parameters.
1612      */
buildRequestPacket(int encap, int transactionId, short secs, Inet4Address clientIp, boolean broadcast, byte[] clientMac, Inet4Address requestedIpAddress, Inet4Address serverIdentifier, byte[] requestedParams, String hostName, List<DhcpOption> options)1613     public static ByteBuffer buildRequestPacket(int encap,
1614             int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
1615             byte[] clientMac, Inet4Address requestedIpAddress,
1616             Inet4Address serverIdentifier, byte[] requestedParams, String hostName,
1617             List<DhcpOption> options) {
1618         DhcpPacket pkt = new DhcpRequestPacket(transactionId, secs, clientIp,
1619                 INADDR_ANY /* relayIp */, clientMac, broadcast);
1620         pkt.mRequestedIp = requestedIpAddress;
1621         pkt.mServerIdentifier = serverIdentifier;
1622         pkt.mHostName = hostName;
1623         pkt.mRequestedParams = requestedParams;
1624         pkt.mCustomizedClientOptions = options;
1625         pkt.mVendorId = getVendorId(options);
1626         ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
1627         return result;
1628     }
1629 
1630     /**
1631      * Builds a DHCP-REQUEST packet from the required specified parameters.
1632      *
1633      * TODO: remove this method when automerger to mainline-prod is running.
1634      */
buildRequestPacket(int encap, int transactionId, short secs, Inet4Address clientIp, boolean broadcast, byte[] clientMac, Inet4Address requestedIpAddress, Inet4Address serverIdentifier, byte[] requestedParams, String hostName)1635     public static ByteBuffer buildRequestPacket(int encap,
1636             int transactionId, short secs, Inet4Address clientIp, boolean broadcast,
1637             byte[] clientMac, Inet4Address requestedIpAddress,
1638             Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
1639         return buildRequestPacket(encap, transactionId, secs, clientIp, broadcast, clientMac,
1640                 requestedIpAddress, serverIdentifier, requestedParams, hostName,
1641                 new ArrayList<DhcpOption>());
1642     }
1643 
1644     /**
1645      * Builds a DHCP-DECLINE packet from the required specified parameters.
1646      */
buildDeclinePacket(int encap, int transactionId, byte[] clientMac, Inet4Address requestedIpAddress, Inet4Address serverIdentifier)1647     public static ByteBuffer buildDeclinePacket(int encap, int transactionId, byte[] clientMac,
1648             Inet4Address requestedIpAddress, Inet4Address serverIdentifier) {
1649         DhcpPacket pkt = new DhcpDeclinePacket(transactionId, (short) 0 /* secs */,
1650                 INADDR_ANY /* clientIp */, INADDR_ANY /* yourIp */, INADDR_ANY /* nextIp */,
1651                 INADDR_ANY /* relayIp */, clientMac, requestedIpAddress, serverIdentifier);
1652         ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
1653         return result;
1654     }
1655 }
1656