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