summaryrefslogtreecommitdiffstats
path: root/services/net
diff options
context:
space:
mode:
authorLorenzo Colitti <lorenzo@google.com>2015-03-10 01:36:14 +0900
committerLorenzo Colitti <lorenzo@google.com>2015-03-20 16:32:36 +0900
commiteca9b9bd8b9a4d0a2de8af8edc20834dd46b98b3 (patch)
tree952aad9cc5d58ce94ddc4625083872f2559e2283 /services/net
parentcbe4f7c225f87ef1e8cb496bce434f334774bc88 (diff)
downloadframeworks_base-eca9b9bd8b9a4d0a2de8af8edc20834dd46b98b3.zip
frameworks_base-eca9b9bd8b9a4d0a2de8af8edc20834dd46b98b3.tar.gz
frameworks_base-eca9b9bd8b9a4d0a2de8af8edc20834dd46b98b3.tar.bz2
DHCP: Move the packet code to frameworks/base/services.
There's no need for it to be in frameworks/base/core, since it will only be used by services. Bug: 19704592 Change-Id: I2f5277eca848b7000ca46db575e8602eacb5c8bd
Diffstat (limited to 'services/net')
-rw-r--r--services/net/Android.mk10
-rw-r--r--services/net/java/android/net/dhcp/DhcpAckPacket.java109
-rw-r--r--services/net/java/android/net/dhcp/DhcpDeclinePacket.java65
-rw-r--r--services/net/java/android/net/dhcp/DhcpDiscoverPacket.java71
-rw-r--r--services/net/java/android/net/dhcp/DhcpInformPacket.java76
-rw-r--r--services/net/java/android/net/dhcp/DhcpNakPacket.java72
-rw-r--r--services/net/java/android/net/dhcp/DhcpOfferPacket.java100
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacket.java896
-rw-r--r--services/net/java/android/net/dhcp/DhcpRequestPacket.java86
-rw-r--r--services/net/java/android/net/dhcp/DhcpStateMachine.java74
10 files changed, 1559 insertions, 0 deletions
diff --git a/services/net/Android.mk b/services/net/Android.mk
new file mode 100644
index 0000000..336bc45
--- /dev/null
+++ b/services/net/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.net
+
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under,java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
new file mode 100644
index 0000000..7b8be9c
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-ACK packet.
+ */
+class DhcpAckPacket extends DhcpPacket {
+
+ /**
+ * The address of the server which sent this packet.
+ */
+ private final InetAddress mSrcIp;
+
+ DhcpAckPacket(int transId, boolean broadcast, InetAddress serverAddress,
+ InetAddress clientIp, byte[] clientMac) {
+ super(transId, Inet4Address.ANY, clientIp, serverAddress,
+ Inet4Address.ANY, clientMac, broadcast);
+ mBroadcast = broadcast;
+ mSrcIp = serverAddress;
+ }
+
+ public String toString() {
+ String s = super.toString();
+ String dnsServers = " DNS servers: ";
+
+ for (InetAddress dnsServer: mDnsServers) {
+ dnsServers += dnsServer.toString() + " ";
+ }
+
+ return s + " ACK: your new IP " + mYourIp +
+ ", netmask " + mSubnetMask +
+ ", gateway " + mGateway + dnsServers +
+ ", lease time " + mLeaseTime;
+ }
+
+ /**
+ * Fills in a packet with the requested ACK parameters.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+ InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
+ InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
+
+ fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
+ DHCP_BOOTREPLY, mBroadcast);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds the optional parameters to the client-generated ACK packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_ACK);
+ addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+ addTlv(buffer, DHCP_LEASE_TIME, mLeaseTime);
+
+ // the client should renew at 1/2 the lease-expiry interval
+ if (mLeaseTime != null) {
+ addTlv(buffer, DHCP_RENEWAL_TIME,
+ Integer.valueOf(mLeaseTime.intValue() / 2));
+ }
+
+ addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
+ addTlv(buffer, DHCP_ROUTER, mGateway);
+ addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
+ addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
+ addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Un-boxes an Integer, returning 0 if a null reference is supplied.
+ */
+ private static final int getInt(Integer v) {
+ if (v == null) {
+ return 0;
+ } else {
+ return v.intValue();
+ }
+ }
+
+ /**
+ * Notifies the specified state machine of the ACK packet parameters.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ machine.onAckReceived(mYourIp, mSubnetMask, mGateway, mDnsServers,
+ mServerIdentifier, getInt(mLeaseTime));
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpDeclinePacket.java b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
new file mode 100644
index 0000000..7646eb4
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-DECLINE packet.
+ */
+class DhcpDeclinePacket extends DhcpPacket {
+ /**
+ * Generates a DECLINE packet with the specified parameters.
+ */
+ DhcpDeclinePacket(int transId, InetAddress clientIp, InetAddress yourIp,
+ InetAddress nextIp, InetAddress relayIp,
+ byte[] clientMac) {
+ super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + " DECLINE";
+ }
+
+ /**
+ * Fills in a packet with the requested DECLINE attributes.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+
+ fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
+ DHCP_BOOTREQUEST, false);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds optional parameters to the DECLINE packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ // None needed
+ }
+
+ /**
+ * Informs the state machine of the arrival of a DECLINE packet.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ machine.onDeclineReceived(mClientMac, mRequestedIp);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
new file mode 100644
index 0000000..0e2d39b
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-DISCOVER packet.
+ */
+class DhcpDiscoverPacket extends DhcpPacket {
+ /**
+ * Generates a DISCOVER packet with the specified parameters.
+ */
+ DhcpDiscoverPacket(int transId, byte[] clientMac, boolean broadcast) {
+ super(transId, Inet4Address.ANY, Inet4Address.ANY, Inet4Address.ANY,
+ Inet4Address.ANY, clientMac, broadcast);
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + " DISCOVER " +
+ (mBroadcast ? "broadcast " : "unicast ");
+ }
+
+ /**
+ * Fills in a packet with the requested DISCOVER parameters.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+ InetAddress destIp = Inet4Address.ALL;
+
+ fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
+ result, DHCP_BOOTREQUEST, true);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds optional parameters to a DISCOVER packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
+ addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Informs the state machine of the arrival of a DISCOVER packet.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ // currently omitted: host name
+ machine.onDiscoverReceived(mBroadcast, mTransId, mClientMac,
+ mRequestedParams);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpInformPacket.java b/services/net/java/android/net/dhcp/DhcpInformPacket.java
new file mode 100644
index 0000000..da73216
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpInformPacket.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the (unused) DHCP-INFORM packet.
+ */
+class DhcpInformPacket extends DhcpPacket {
+ /**
+ * Generates an INFORM packet with the specified parameters.
+ */
+ DhcpInformPacket(int transId, InetAddress clientIp, InetAddress yourIp,
+ InetAddress nextIp, InetAddress relayIp,
+ byte[] clientMac) {
+ super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + " INFORM";
+ }
+
+ /**
+ * Builds an INFORM packet.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+
+ fillInPacket(encap, mClientIp, mYourIp, destUdp, srcUdp, result,
+ DHCP_BOOTREQUEST, false);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds additional parameters to the INFORM packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ byte[] clientId = new byte[7];
+
+ clientId[0] = CLIENT_ID_ETHER;
+ System.arraycopy(mClientMac, 0, clientId, 1, 6);
+
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
+ addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Informs the state machine of the arrival of an INFORM packet. Not
+ * used currently.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ InetAddress clientRequest =
+ mRequestedIp == null ? mClientIp : mRequestedIp;
+ machine.onInformReceived(mTransId, mClientMac, clientRequest,
+ mRequestedParams);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
new file mode 100644
index 0000000..1f340ad
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-NAK packet.
+ */
+class DhcpNakPacket extends DhcpPacket {
+ /**
+ * Generates a NAK packet with the specified parameters.
+ */
+ DhcpNakPacket(int transId, InetAddress clientIp, InetAddress yourIp,
+ InetAddress nextIp, InetAddress relayIp,
+ byte[] clientMac) {
+ super(transId, Inet4Address.ANY, Inet4Address.ANY, nextIp, relayIp,
+ clientMac, false);
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + " NAK, reason " + (mMessage == null ? "(none)" : mMessage);
+ }
+
+ /**
+ * Fills in a packet with the requested NAK attributes.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+ InetAddress destIp = mClientIp;
+ InetAddress srcIp = mYourIp;
+
+ fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
+ DHCP_BOOTREPLY, mBroadcast);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds the optional parameters to the client-generated NAK packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_NAK);
+ addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+ addTlv(buffer, DHCP_MESSAGE, mMessage);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Notifies the specified state machine of the newly-arrived NAK packet.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ machine.onNakReceived();
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
new file mode 100644
index 0000000..f1c30e1
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-OFFER packet.
+ */
+class DhcpOfferPacket extends DhcpPacket {
+ /**
+ * The IP address of the server which sent this packet.
+ */
+ private final InetAddress mSrcIp;
+
+ /**
+ * Generates a OFFER packet with the specified parameters.
+ */
+ DhcpOfferPacket(int transId, boolean broadcast, InetAddress serverAddress,
+ InetAddress clientIp, byte[] clientMac) {
+ super(transId, Inet4Address.ANY, clientIp, Inet4Address.ANY,
+ Inet4Address.ANY, clientMac, broadcast);
+ mSrcIp = serverAddress;
+ }
+
+ public String toString() {
+ String s = super.toString();
+ String dnsServers = ", DNS servers: ";
+
+ if (mDnsServers != null) {
+ for (InetAddress dnsServer: mDnsServers) {
+ dnsServers += dnsServer + " ";
+ }
+ }
+
+ return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask +
+ dnsServers + ", gateway " + mGateway +
+ " lease time " + mLeaseTime + ", domain " + mDomainName;
+ }
+
+ /**
+ * Fills in a packet with the specified OFFER attributes.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+ InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
+ InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
+
+ fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
+ DHCP_BOOTREPLY, mBroadcast);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds the optional parameters to the server-generated OFFER packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_OFFER);
+ addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+ addTlv(buffer, DHCP_LEASE_TIME, mLeaseTime);
+
+ // the client should renew at 1/2 the lease-expiry interval
+ if (mLeaseTime != null) {
+ addTlv(buffer, DHCP_RENEWAL_TIME,
+ Integer.valueOf(mLeaseTime.intValue() / 2));
+ }
+
+ addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask);
+ addTlv(buffer, DHCP_ROUTER, mGateway);
+ addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName);
+ addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress);
+ addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Notifies the state machine of the OFFER packet parameters.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ machine.onOfferReceived(mBroadcast, mTransId, mClientMac, mYourIp,
+ mServerIdentifier);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
new file mode 100644
index 0000000..68108fe
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -0,0 +1,896 @@
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.nio.ShortBuffer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Defines basic data and operations needed to build and use packets for the
+ * DHCP protocol. Subclasses create the specific packets used at each
+ * stage of the negotiation.
+ */
+abstract class DhcpPacket {
+ protected static final String TAG = "DhcpPacket";
+
+ /**
+ * Packet encapsulations.
+ */
+ public static final int ENCAP_L2 = 0; // EthernetII header included
+ public static final int ENCAP_L3 = 1; // IP/UDP header included
+ public static final int ENCAP_BOOTP = 2; // BOOTP contents only
+
+ /**
+ * IP layer definitions.
+ */
+ private static final byte IP_TYPE_UDP = (byte) 0x11;
+
+ /**
+ * IP: Version 4, Header Length 20 bytes
+ */
+ private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
+
+ /**
+ * IP: Flags 0, Fragment Offset 0, Don't Fragment
+ */
+ private static final short IP_FLAGS_OFFSET = (short) 0x4000;
+
+ /**
+ * IP: TOS
+ */
+ private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
+
+ /**
+ * IP: TTL -- use default 64 from RFC1340
+ */
+ private static final byte IP_TTL = (byte) 0x40;
+
+ /**
+ * The client DHCP port.
+ */
+ static final short DHCP_CLIENT = (short) 68;
+
+ /**
+ * The server DHCP port.
+ */
+ static final short DHCP_SERVER = (short) 67;
+
+ /**
+ * The message op code indicating a request from a client.
+ */
+ protected static final byte DHCP_BOOTREQUEST = (byte) 1;
+
+ /**
+ * The message op code indicating a response from the server.
+ */
+ protected static final byte DHCP_BOOTREPLY = (byte) 2;
+
+ /**
+ * The code type used to identify an Ethernet MAC address in the
+ * Client-ID field.
+ */
+ protected static final byte CLIENT_ID_ETHER = (byte) 1;
+
+ /**
+ * The maximum length of a packet that can be constructed.
+ */
+ protected static final int MAX_LENGTH = 1500;
+
+ /**
+ * DHCP Optional Type: DHCP Subnet Mask
+ */
+ protected static final byte DHCP_SUBNET_MASK = 1;
+ protected InetAddress mSubnetMask;
+
+ /**
+ * DHCP Optional Type: DHCP Router
+ */
+ protected static final byte DHCP_ROUTER = 3;
+ protected InetAddress mGateway;
+
+ /**
+ * DHCP Optional Type: DHCP DNS Server
+ */
+ protected static final byte DHCP_DNS_SERVER = 6;
+ protected List<InetAddress> mDnsServers;
+
+ /**
+ * DHCP Optional Type: DHCP Host Name
+ */
+ protected static final byte DHCP_HOST_NAME = 12;
+ protected String mHostName;
+
+ /**
+ * DHCP Optional Type: DHCP DOMAIN NAME
+ */
+ protected static final byte DHCP_DOMAIN_NAME = 15;
+ protected String mDomainName;
+
+ /**
+ * DHCP Optional Type: DHCP BROADCAST ADDRESS
+ */
+ protected static final byte DHCP_BROADCAST_ADDRESS = 28;
+ protected InetAddress mBroadcastAddress;
+
+ /**
+ * DHCP Optional Type: DHCP Requested IP Address
+ */
+ protected static final byte DHCP_REQUESTED_IP = 50;
+ protected InetAddress mRequestedIp;
+
+ /**
+ * DHCP Optional Type: DHCP Lease Time
+ */
+ protected static final byte DHCP_LEASE_TIME = 51;
+ protected Integer mLeaseTime;
+
+ /**
+ * DHCP Optional Type: DHCP Message Type
+ */
+ protected static final byte DHCP_MESSAGE_TYPE = 53;
+ // the actual type values
+ protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
+ protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
+ protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
+ protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
+ protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
+ protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
+ protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
+
+ /**
+ * DHCP Optional Type: DHCP Server Identifier
+ */
+ protected static final byte DHCP_SERVER_IDENTIFIER = 54;
+ protected InetAddress mServerIdentifier;
+
+ /**
+ * DHCP Optional Type: DHCP Parameter List
+ */
+ protected static final byte DHCP_PARAMETER_LIST = 55;
+ protected byte[] mRequestedParams;
+
+ /**
+ * DHCP Optional Type: DHCP MESSAGE
+ */
+ protected static final byte DHCP_MESSAGE = 56;
+ protected String mMessage;
+
+ /**
+ * DHCP Optional Type: DHCP Renewal Time Value
+ */
+ protected static final byte DHCP_RENEWAL_TIME = 58;
+
+ /**
+ * DHCP Optional Type: Vendor Class Identifier
+ */
+ protected static final byte DHCP_VENDOR_CLASS_ID = 60;
+
+ /**
+ * DHCP Optional Type: DHCP Client Identifier
+ */
+ protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
+
+ /**
+ * The transaction identifier used in this particular DHCP negotiation
+ */
+ protected final int mTransId;
+
+ /**
+ * The IP address of the client host. This address is typically
+ * proposed by the client (from an earlier DHCP negotiation) or
+ * supplied by the server.
+ */
+ protected final InetAddress mClientIp;
+ protected final InetAddress mYourIp;
+ private final InetAddress mNextIp;
+ private final InetAddress mRelayIp;
+
+ /**
+ * Does the client request a broadcast response?
+ */
+ protected boolean mBroadcast;
+
+ /**
+ * The six-octet MAC of the client.
+ */
+ protected final byte[] mClientMac;
+
+ /**
+ * Asks the packet object to signal the next operation in the DHCP
+ * protocol. The available actions are methods defined in the
+ * DhcpStateMachine interface.
+ */
+ public abstract void doNextOp(DhcpStateMachine stateMachine);
+
+ /**
+ * Asks the packet object to create a ByteBuffer serialization of
+ * the packet for transmission.
+ */
+ public abstract ByteBuffer buildPacket(int encap, short destUdp,
+ short srcUdp);
+
+ /**
+ * Allows the concrete class to fill in packet-type-specific details,
+ * typically optional parameters at the end of the packet.
+ */
+ abstract void finishPacket(ByteBuffer buffer);
+
+ protected DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp,
+ InetAddress nextIp, InetAddress relayIp,
+ byte[] clientMac, boolean broadcast) {
+ mTransId = transId;
+ mClientIp = clientIp;
+ mYourIp = yourIp;
+ mNextIp = nextIp;
+ mRelayIp = relayIp;
+ mClientMac = clientMac;
+ mBroadcast = broadcast;
+ }
+
+ /**
+ * Returns the transaction ID.
+ */
+ public int getTransactionId() {
+ return mTransId;
+ }
+
+ /**
+ * Creates a new L3 packet (including IP header) containing the
+ * DHCP udp packet. This method relies upon the delegated method
+ * finishPacket() to insert the per-packet contents.
+ */
+ protected void fillInPacket(int encap, InetAddress destIp,
+ InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf,
+ byte requestCode, boolean broadcast) {
+ byte[] destIpArray = destIp.getAddress();
+ byte[] srcIpArray = srcIp.getAddress();
+ int ipLengthOffset = 0;
+ int ipChecksumOffset = 0;
+ int endIpHeader = 0;
+ int udpHeaderOffset = 0;
+ int udpLengthOffset = 0;
+ int udpChecksumOffset = 0;
+
+ buf.clear();
+ buf.order(ByteOrder.BIG_ENDIAN);
+
+ // if a full IP packet needs to be generated, put the IP & UDP
+ // headers in place, and pre-populate with artificial values
+ // needed to seed the IP checksum.
+ if (encap == ENCAP_L3) {
+ // fake IP header, used in the IP-header checksum
+ buf.put(IP_VERSION_HEADER_LEN);
+ buf.put(IP_TOS_LOWDELAY); // tos: IPTOS_LOWDELAY
+ ipLengthOffset = buf.position();
+ buf.putShort((short)0); // length
+ buf.putShort((short)0); // id
+ buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
+ buf.put(IP_TTL); // TTL: use default 64 from RFC1340
+ buf.put(IP_TYPE_UDP);
+ ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // checksum
+
+ buf.put(srcIpArray);
+ buf.put(destIpArray);
+ endIpHeader = buf.position();
+
+ // UDP header
+ udpHeaderOffset = buf.position();
+ buf.putShort(srcUdp);
+ buf.putShort(destUdp);
+ udpLengthOffset = buf.position();
+ buf.putShort((short) 0); // length
+ udpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // UDP checksum -- initially zero
+ }
+
+ // DHCP payload
+ buf.put(requestCode);
+ buf.put((byte) 1); // Hardware Type: Ethernet
+ buf.put((byte) mClientMac.length); // Hardware Address Length
+ buf.put((byte) 0); // Hop Count
+ buf.putInt(mTransId); // Transaction ID
+ buf.putShort((short) 0); // Elapsed Seconds
+
+ if (broadcast) {
+ buf.putShort((short) 0x8000); // Flags
+ } else {
+ buf.putShort((short) 0x0000); // Flags
+ }
+
+ buf.put(mClientIp.getAddress());
+ buf.put(mYourIp.getAddress());
+ buf.put(mNextIp.getAddress());
+ buf.put(mRelayIp.getAddress());
+ buf.put(mClientMac);
+ buf.position(buf.position() +
+ (16 - mClientMac.length) // pad addr to 16 bytes
+ + 64 // empty server host name (64 bytes)
+ + 128); // empty boot file name (128 bytes)
+ buf.putInt(0x63825363); // magic number
+ finishPacket(buf);
+
+ // round up to an even number of octets
+ if ((buf.position() & 1) == 1) {
+ buf.put((byte) 0);
+ }
+
+ // If an IP packet is being built, the IP & UDP checksums must be
+ // computed.
+ if (encap == ENCAP_L3) {
+ // fix UDP header: insert length
+ short udpLen = (short)(buf.position() - udpHeaderOffset);
+ buf.putShort(udpLengthOffset, udpLen);
+ // fix UDP header: checksum
+ // checksum for UDP at udpChecksumOffset
+ int udpSeed = 0;
+
+ // apply IPv4 pseudo-header. Read IP address src and destination
+ // values from the IP header and accumulate checksum.
+ udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
+ udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
+ udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
+ udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
+
+ // accumulate extra data for the pseudo-header
+ udpSeed += IP_TYPE_UDP;
+ udpSeed += udpLen;
+ // and compute UDP checksum
+ buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
+ udpHeaderOffset,
+ buf.position()));
+ // fix IP header: insert length
+ buf.putShort(ipLengthOffset, (short)buf.position());
+ // fixup IP-header checksum
+ buf.putShort(ipChecksumOffset,
+ (short) checksum(buf, 0, 0, endIpHeader));
+ }
+ }
+
+ /**
+ * Converts a signed short value to an unsigned int value. Needed
+ * because Java does not have unsigned types.
+ */
+ private int intAbs(short v) {
+ if (v < 0) {
+ int r = v + 65536;
+ return r;
+ } else {
+ return(v);
+ }
+ }
+
+ /**
+ * Performs an IP checksum (used in IP header and across UDP
+ * payload) on the specified portion of a ByteBuffer. The seed
+ * allows the checksum to commence with a specified value.
+ */
+ private int checksum(ByteBuffer buf, int seed, int start, int end) {
+ int sum = seed;
+ int bufPosition = buf.position();
+
+ // set position of original ByteBuffer, so that the ShortBuffer
+ // will be correctly initialized
+ buf.position(start);
+ ShortBuffer shortBuf = buf.asShortBuffer();
+
+ // re-set ByteBuffer position
+ buf.position(bufPosition);
+
+ short[] shortArray = new short[(end - start) / 2];
+ shortBuf.get(shortArray);
+
+ for (short s : shortArray) {
+ sum += intAbs(s);
+ }
+
+ start += shortArray.length * 2;
+
+ // see if a singleton byte remains
+ if (end != start) {
+ short b = buf.get(start);
+
+ // make it unsigned
+ if (b < 0) {
+ b += 256;
+ }
+
+ sum += b * 256;
+ }
+
+ sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
+ sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
+ int negated = ~sum;
+ return intAbs((short) negated);
+ }
+
+ /**
+ * Adds an optional parameter containing a single byte value.
+ */
+ protected void addTlv(ByteBuffer buf, byte type, byte value) {
+ buf.put(type);
+ buf.put((byte) 1);
+ buf.put(value);
+ }
+
+ /**
+ * Adds an optional parameter containing an array of bytes.
+ */
+ protected void addTlv(ByteBuffer buf, byte type, byte[] payload) {
+ if (payload != null) {
+ buf.put(type);
+ buf.put((byte) payload.length);
+ buf.put(payload);
+ }
+ }
+
+ /**
+ * Adds an optional parameter containing an IP address.
+ */
+ protected void addTlv(ByteBuffer buf, byte type, InetAddress addr) {
+ if (addr != null) {
+ addTlv(buf, type, addr.getAddress());
+ }
+ }
+
+ /**
+ * Adds an optional parameter containing a list of IP addresses.
+ */
+ protected void addTlv(ByteBuffer buf, byte type, List<InetAddress> addrs) {
+ if (addrs != null && addrs.size() > 0) {
+ buf.put(type);
+ buf.put((byte)(4 * addrs.size()));
+
+ for (InetAddress addr : addrs) {
+ buf.put(addr.getAddress());
+ }
+ }
+ }
+
+ /**
+ * Adds an optional parameter containing a simple integer
+ */
+ protected void addTlv(ByteBuffer buf, byte type, Integer value) {
+ if (value != null) {
+ buf.put(type);
+ buf.put((byte) 4);
+ buf.putInt(value.intValue());
+ }
+ }
+
+ /**
+ * Adds an optional parameter containing and ASCII string.
+ */
+ protected void addTlv(ByteBuffer buf, byte type, String str) {
+ if (str != null) {
+ buf.put(type);
+ buf.put((byte) str.length());
+
+ for (int i = 0; i < str.length(); i++) {
+ buf.put((byte) str.charAt(i));
+ }
+ }
+ }
+
+ /**
+ * Adds the special end-of-optional-parameters indicator.
+ */
+ protected void addTlvEnd(ByteBuffer buf) {
+ buf.put((byte) 0xFF);
+ }
+
+ /**
+ * Converts a MAC from an array of octets to an ASCII string.
+ */
+ public static String macToString(byte[] mac) {
+ String macAddr = "";
+
+ for (int i = 0; i < mac.length; i++) {
+ String hexString = "0" + Integer.toHexString(mac[i]);
+
+ // substring operation grabs the last 2 digits: this
+ // allows signed bytes to be converted correctly.
+ macAddr += hexString.substring(hexString.length() - 2);
+
+ if (i != (mac.length - 1)) {
+ macAddr += ":";
+ }
+ }
+
+ return macAddr;
+ }
+
+ public String toString() {
+ String macAddr = macToString(mClientMac);
+
+ return macAddr;
+ }
+
+ /**
+ * Reads a four-octet value from a ByteBuffer and construct
+ * an IPv4 address from that value.
+ */
+ private static InetAddress readIpAddress(ByteBuffer packet) {
+ InetAddress result = null;
+ byte[] ipAddr = new byte[4];
+ packet.get(ipAddr);
+
+ try {
+ result = InetAddress.getByAddress(ipAddr);
+ } catch (UnknownHostException ex) {
+ // ipAddr is numeric, so this should not be
+ // triggered. However, if it is, just nullify
+ result = null;
+ }
+
+ return result;
+ }
+
+ /**
+ * Reads a string of specified length from the buffer.
+ */
+ private static String readAsciiString(ByteBuffer buf, int byteCount) {
+ byte[] bytes = new byte[byteCount];
+ buf.get(bytes);
+ return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
+ }
+
+ /**
+ * Creates a concrete DhcpPacket from the supplied ByteBuffer. The
+ * buffer may have an L2 encapsulation (which is the full EthernetII
+ * format starting with the source-address MAC) or an L3 encapsulation
+ * (which starts with the IP header).
+ * <br>
+ * A subset of the optional parameters are parsed and are stored
+ * in object fields.
+ */
+ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
+ {
+ // bootp parameters
+ int transactionId;
+ InetAddress clientIp;
+ InetAddress yourIp;
+ InetAddress nextIp;
+ InetAddress relayIp;
+ byte[] clientMac;
+ List<InetAddress> dnsServers = new ArrayList<InetAddress>();
+ InetAddress gateway = null; // aka router
+ Integer leaseTime = null;
+ InetAddress serverIdentifier = null;
+ InetAddress netMask = null;
+ String message = null;
+ String vendorId = null;
+ byte[] expectedParams = null;
+ String hostName = null;
+ String domainName = null;
+ InetAddress ipSrc = null;
+ InetAddress ipDst = null;
+ InetAddress bcAddr = null;
+ InetAddress requestedIp = null;
+
+ // dhcp options
+ byte dhcpType = (byte) 0xFF;
+
+ packet.order(ByteOrder.BIG_ENDIAN);
+
+ // check to see if we need to parse L2, IP, and UDP encaps
+ if (pktType == ENCAP_L2) {
+ // System.out.println("buffer len " + packet.limit());
+ byte[] l2dst = new byte[6];
+ byte[] l2src = new byte[6];
+
+ packet.get(l2dst);
+ packet.get(l2src);
+
+ short l2type = packet.getShort();
+
+ if (l2type != 0x0800)
+ return null;
+ }
+
+ if ((pktType == ENCAP_L2) || (pktType == ENCAP_L3)) {
+ // assume l2type is 0x0800, i.e. IP
+ byte ipType = packet.get();
+ // System.out.println("ipType is " + ipType);
+ byte ipDiffServicesField = packet.get();
+ short ipTotalLength = packet.getShort();
+ short ipIdentification = packet.getShort();
+ byte ipFlags = packet.get();
+ byte ipFragOffset = packet.get();
+ byte ipTTL = packet.get();
+ byte ipProto = packet.get();
+ short ipChksm = packet.getShort();
+
+ ipSrc = readIpAddress(packet);
+ ipDst = readIpAddress(packet);
+
+ if (ipProto != IP_TYPE_UDP) // UDP
+ return null;
+
+ // assume UDP
+ short udpSrcPort = packet.getShort();
+ short udpDstPort = packet.getShort();
+ short udpLen = packet.getShort();
+ short udpChkSum = packet.getShort();
+
+ if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
+ return null;
+ }
+
+ // assume bootp
+ byte type = packet.get();
+ byte hwType = packet.get();
+ byte addrLen = packet.get();
+ byte hops = packet.get();
+ transactionId = packet.getInt();
+ short elapsed = packet.getShort();
+ short bootpFlags = packet.getShort();
+ boolean broadcast = (bootpFlags & 0x8000) != 0;
+ byte[] ipv4addr = new byte[4];
+
+ try {
+ packet.get(ipv4addr);
+ clientIp = InetAddress.getByAddress(ipv4addr);
+ packet.get(ipv4addr);
+ yourIp = InetAddress.getByAddress(ipv4addr);
+ packet.get(ipv4addr);
+ nextIp = InetAddress.getByAddress(ipv4addr);
+ packet.get(ipv4addr);
+ relayIp = InetAddress.getByAddress(ipv4addr);
+ } catch (UnknownHostException ex) {
+ return null;
+ }
+
+ clientMac = new byte[addrLen];
+ packet.get(clientMac);
+
+ // skip over address padding (16 octets allocated)
+ packet.position(packet.position() + (16 - addrLen)
+ + 64 // skip server host name (64 chars)
+ + 128); // skip boot file name (128 chars)
+
+ int dhcpMagicCookie = packet.getInt();
+
+ if (dhcpMagicCookie != 0x63825363)
+ return null;
+
+ // parse options
+ boolean notFinishedOptions = true;
+
+ while ((packet.position() < packet.limit()) && notFinishedOptions) {
+ byte optionType = packet.get();
+
+ if (optionType == (byte) 0xFF) {
+ notFinishedOptions = false;
+ } else {
+ byte optionLen = packet.get();
+ int expectedLen = 0;
+
+ switch(optionType) {
+ case DHCP_SUBNET_MASK:
+ netMask = readIpAddress(packet);
+ expectedLen = 4;
+ break;
+ case DHCP_ROUTER:
+ gateway = readIpAddress(packet);
+ expectedLen = 4;
+ break;
+ case DHCP_DNS_SERVER:
+ expectedLen = 0;
+
+ for (expectedLen = 0; expectedLen < optionLen;
+ expectedLen += 4) {
+ dnsServers.add(readIpAddress(packet));
+ }
+ break;
+ case DHCP_HOST_NAME:
+ expectedLen = optionLen;
+ hostName = readAsciiString(packet, optionLen);
+ break;
+ case DHCP_DOMAIN_NAME:
+ expectedLen = optionLen;
+ domainName = readAsciiString(packet, optionLen);
+ break;
+ case DHCP_BROADCAST_ADDRESS:
+ bcAddr = readIpAddress(packet);
+ expectedLen = 4;
+ break;
+ case DHCP_REQUESTED_IP:
+ requestedIp = readIpAddress(packet);
+ expectedLen = 4;
+ break;
+ case DHCP_LEASE_TIME:
+ leaseTime = Integer.valueOf(packet.getInt());
+ expectedLen = 4;
+ break;
+ case DHCP_MESSAGE_TYPE:
+ dhcpType = packet.get();
+ expectedLen = 1;
+ break;
+ case DHCP_SERVER_IDENTIFIER:
+ serverIdentifier = readIpAddress(packet);
+ expectedLen = 4;
+ break;
+ case DHCP_PARAMETER_LIST:
+ expectedParams = new byte[optionLen];
+ packet.get(expectedParams);
+ expectedLen = optionLen;
+ break;
+ case DHCP_MESSAGE:
+ expectedLen = optionLen;
+ message = readAsciiString(packet, optionLen);
+ break;
+ case DHCP_VENDOR_CLASS_ID:
+ expectedLen = optionLen;
+ vendorId = readAsciiString(packet, optionLen);
+ break;
+ case DHCP_CLIENT_IDENTIFIER: { // Client identifier
+ byte[] id = new byte[optionLen];
+ packet.get(id);
+ expectedLen = optionLen;
+ } break;
+ default:
+ // ignore any other parameters
+ for (int i = 0; i < optionLen; i++) {
+ expectedLen++;
+ byte throwaway = packet.get();
+ }
+ }
+
+ if (expectedLen != optionLen) {
+ return null;
+ }
+ }
+ }
+
+ DhcpPacket newPacket;
+
+ switch(dhcpType) {
+ case -1: return null;
+ case DHCP_MESSAGE_TYPE_DISCOVER:
+ newPacket = new DhcpDiscoverPacket(
+ transactionId, clientMac, broadcast);
+ break;
+ case DHCP_MESSAGE_TYPE_OFFER:
+ newPacket = new DhcpOfferPacket(
+ transactionId, broadcast, ipSrc, yourIp, clientMac);
+ break;
+ case DHCP_MESSAGE_TYPE_REQUEST:
+ newPacket = new DhcpRequestPacket(
+ transactionId, clientIp, clientMac, broadcast);
+ break;
+ case DHCP_MESSAGE_TYPE_DECLINE:
+ newPacket = new DhcpDeclinePacket(
+ transactionId, clientIp, yourIp, nextIp, relayIp,
+ clientMac);
+ break;
+ case DHCP_MESSAGE_TYPE_ACK:
+ newPacket = new DhcpAckPacket(
+ transactionId, broadcast, ipSrc, yourIp, clientMac);
+ break;
+ case DHCP_MESSAGE_TYPE_NAK:
+ newPacket = new DhcpNakPacket(
+ transactionId, clientIp, yourIp, nextIp, relayIp,
+ clientMac);
+ break;
+ case DHCP_MESSAGE_TYPE_INFORM:
+ newPacket = new DhcpInformPacket(
+ transactionId, clientIp, yourIp, nextIp, relayIp,
+ clientMac);
+ break;
+ default:
+ System.out.println("Unimplemented type: " + dhcpType);
+ return null;
+ }
+
+ newPacket.mBroadcastAddress = bcAddr;
+ newPacket.mDnsServers = dnsServers;
+ newPacket.mDomainName = domainName;
+ newPacket.mGateway = gateway;
+ newPacket.mHostName = hostName;
+ newPacket.mLeaseTime = leaseTime;
+ newPacket.mMessage = message;
+ newPacket.mRequestedIp = requestedIp;
+ newPacket.mRequestedParams = expectedParams;
+ newPacket.mServerIdentifier = serverIdentifier;
+ newPacket.mSubnetMask = netMask;
+ return newPacket;
+ }
+
+ /**
+ * Parse a packet from an array of bytes.
+ */
+ public static DhcpPacket decodeFullPacket(byte[] packet, int pktType)
+ {
+ ByteBuffer buffer = ByteBuffer.wrap(packet).order(ByteOrder.BIG_ENDIAN);
+ return decodeFullPacket(buffer, pktType);
+ }
+
+ /**
+ * Builds a DHCP-DISCOVER packet from the required specified
+ * parameters.
+ */
+ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
+ byte[] clientMac, boolean broadcast, byte[] expectedParams) {
+ DhcpPacket pkt = new DhcpDiscoverPacket(
+ transactionId, clientMac, broadcast);
+ pkt.mRequestedParams = expectedParams;
+ return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
+ }
+
+ /**
+ * Builds a DHCP-OFFER packet from the required specified
+ * parameters.
+ */
+ public static ByteBuffer buildOfferPacket(int encap, int transactionId,
+ boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
+ byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
+ InetAddress gateway, List<InetAddress> dnsServers,
+ InetAddress dhcpServerIdentifier, String domainName) {
+ DhcpPacket pkt = new DhcpOfferPacket(
+ transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+ pkt.mGateway = gateway;
+ pkt.mDnsServers = dnsServers;
+ pkt.mLeaseTime = timeout;
+ pkt.mDomainName = domainName;
+ pkt.mServerIdentifier = dhcpServerIdentifier;
+ pkt.mSubnetMask = netMask;
+ pkt.mBroadcastAddress = bcAddr;
+ return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+ }
+
+ /**
+ * Builds a DHCP-ACK packet from the required specified parameters.
+ */
+ public static ByteBuffer buildAckPacket(int encap, int transactionId,
+ boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
+ byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
+ InetAddress gateway, List<InetAddress> dnsServers,
+ InetAddress dhcpServerIdentifier, String domainName) {
+ DhcpPacket pkt = new DhcpAckPacket(
+ transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+ pkt.mGateway = gateway;
+ pkt.mDnsServers = dnsServers;
+ pkt.mLeaseTime = timeout;
+ pkt.mDomainName = domainName;
+ pkt.mSubnetMask = netMask;
+ pkt.mServerIdentifier = dhcpServerIdentifier;
+ pkt.mBroadcastAddress = bcAddr;
+ return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+ }
+
+ /**
+ * Builds a DHCP-NAK packet from the required specified parameters.
+ */
+ public static ByteBuffer buildNakPacket(int encap, int transactionId,
+ InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac) {
+ DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
+ serverIpAddr, serverIpAddr, serverIpAddr, mac);
+ pkt.mMessage = "requested address not available";
+ pkt.mRequestedIp = clientIpAddr;
+ return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+ }
+
+ /**
+ * Builds a DHCP-REQUEST packet from the required specified parameters.
+ */
+ public static ByteBuffer buildRequestPacket(int encap,
+ int transactionId, InetAddress clientIp, boolean broadcast,
+ byte[] clientMac, InetAddress requestedIpAddress,
+ InetAddress serverIdentifier, byte[] requestedParams, String hostName) {
+ DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
+ clientMac, broadcast);
+ pkt.mRequestedIp = requestedIpAddress;
+ pkt.mServerIdentifier = serverIdentifier;
+ pkt.mHostName = hostName;
+ pkt.mRequestedParams = requestedParams;
+ ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
+ return result;
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
new file mode 100644
index 0000000..cf32957
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+/**
+ * This class implements the DHCP-REQUEST packet.
+ */
+class DhcpRequestPacket extends DhcpPacket {
+ /**
+ * Generates a REQUEST packet with the specified parameters.
+ */
+ DhcpRequestPacket(int transId, InetAddress clientIp, byte[] clientMac,
+ boolean broadcast) {
+ super(transId, clientIp, Inet4Address.ANY, Inet4Address.ANY,
+ Inet4Address.ANY, clientMac, broadcast);
+ }
+
+ public String toString() {
+ String s = super.toString();
+ return s + " REQUEST, desired IP " + mRequestedIp + " from host '"
+ + mHostName + "', param list length "
+ + (mRequestedParams == null ? 0 : mRequestedParams.length);
+ }
+
+ /**
+ * Fills in a packet with the requested REQUEST attributes.
+ */
+ public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
+ ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+
+ fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
+ result, DHCP_BOOTREQUEST, mBroadcast);
+ result.flip();
+ return result;
+ }
+
+ /**
+ * Adds the optional parameters to the client-generated REQUEST packet.
+ */
+ void finishPacket(ByteBuffer buffer) {
+ byte[] clientId = new byte[7];
+
+ // assemble client identifier
+ clientId[0] = CLIENT_ID_ETHER;
+ System.arraycopy(mClientMac, 0, clientId, 1, 6);
+
+ addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
+ addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
+ addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
+ addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+ addTlv(buffer, DHCP_CLIENT_IDENTIFIER, clientId);
+ addTlvEnd(buffer);
+ }
+
+ /**
+ * Notifies the specified state machine of the REQUEST packet parameters.
+ */
+ public void doNextOp(DhcpStateMachine machine) {
+ InetAddress clientRequest =
+ mRequestedIp == null ? mClientIp : mRequestedIp;
+ Log.v(TAG, "requested IP is " + mRequestedIp + " and client IP is " +
+ mClientIp);
+ machine.onRequestReceived(mBroadcast, mTransId, mClientMac,
+ clientRequest, mRequestedParams, mHostName);
+ }
+}
diff --git a/services/net/java/android/net/dhcp/DhcpStateMachine.java b/services/net/java/android/net/dhcp/DhcpStateMachine.java
new file mode 100644
index 0000000..bc9a798
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpStateMachine.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.dhcp;
+
+import java.net.InetAddress;
+import java.util.List;
+
+/**
+ * This class defines the "next steps" which occur after a given DHCP
+ * packet has been received.
+ */
+interface DhcpStateMachine {
+ /**
+ * Signals that an offer packet has been received with the specified
+ * parameters.
+ */
+ public void onOfferReceived(boolean broadcast, int transactionId,
+ byte[] myMac, InetAddress offeredIpAddress,
+ InetAddress serverIpAddress);
+
+ /**
+ * Signals that a NAK packet has been received.
+ */
+ public void onNakReceived();
+
+ /**
+ * Signals that the final ACK has been received from the server.
+ */
+ public void onAckReceived(InetAddress myIpAddress, InetAddress myNetMask,
+ InetAddress myGateway, List<InetAddress> myDnsServers,
+ InetAddress myDhcpServer, int leaseTime);
+
+ /**
+ * Signals that a client's DISCOVER packet has been received with the
+ * specified parameters.
+ */
+ public void onDiscoverReceived(boolean broadcast, int transactionId,
+ byte[] clientMac, byte[] requestedParameterList);
+
+ /**
+ * Signals that a client's REQUEST packet has been received with the
+ * specified parameters.
+ */
+ public void onRequestReceived(boolean broadcast, int transactionId,
+ byte[] clientMac, InetAddress requestedIp, byte[] requestedParams,
+ String clientHostName);
+
+ /**
+ * Signals that a client's INFORM packet has been received with the
+ * specified parameters.
+ */
+ public void onInformReceived(int transactionId, byte[] clientMac,
+ InetAddress preassignedIp, byte[] requestedParams);
+
+ /**
+ * Signals that a client's DECLINE packet has been received with the
+ * specified parameters.
+ */
+ public void onDeclineReceived(byte[] clientMac, InetAddress declinedIp);
+}