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