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