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