aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java')
-rw-r--r--framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java632
1 files changed, 632 insertions, 0 deletions
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
new file mode 100644
index 00000000..de5b43fa
--- /dev/null
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/DHCP.java
@@ -0,0 +1,632 @@
+/*
+ * Copyright 2014 Open Networking Laboratory
+ *
+ * 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 org.onlab.packet;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static org.onlab.packet.PacketUtils.checkInput;
+
+/**
+ *
+ */
+public class DHCP extends BasePacket {
+ /**
+ * Dynamic Host Configuration Protocol packet.
+ * ------------------------------------------ |op (1) | htype(1) | hlen(1) |
+ * hops(1) | ------------------------------------------ | xid (4) |
+ * ------------------------------------------ | secs (2) | flags (2) |
+ * ------------------------------------------ | ciaddr (4) |
+ * ------------------------------------------ | yiaddr (4) |
+ * ------------------------------------------ | siaddr (4) |
+ * ------------------------------------------ | giaddr (4) |
+ * ------------------------------------------ | chaddr (16) |
+ * ------------------------------------------ | sname (64) |
+ * ------------------------------------------ | file (128) |
+ * ------------------------------------------ | options (312) |
+ * ------------------------------------------
+ *
+ */
+ // Header + magic without options
+ public static final int MIN_HEADER_LENGTH = 240;
+ public static final byte OPCODE_REQUEST = 0x1;
+ public static final byte OPCODE_REPLY = 0x2;
+
+ public static final byte HWTYPE_ETHERNET = 0x1;
+
+ public enum DHCPOptionCode {
+ OptionCode_SubnetMask((byte) 1), OptionCode_RouterAddress((byte) 3), OptionCode_DomainServer((byte) 6),
+ OptionCode_HostName((byte) 12), OptionCode_DomainName((byte) 15), OptionCode_BroadcastAddress((byte) 28),
+ OptionCode_RequestedIP((byte) 50), OptionCode_LeaseTime((byte) 51), OptionCode_MessageType((byte) 53),
+ OptionCode_DHCPServerIp((byte) 54), OptionCode_RequestedParameters((byte) 55),
+ OptionCode_RenewalTime((byte) 58), OPtionCode_RebindingTime((byte) 59), OptionCode_ClientID((byte) 61),
+ OptionCode_END((byte) 255);
+
+ protected byte value;
+
+ private DHCPOptionCode(final byte value) {
+ this.value = value;
+ }
+
+ public byte getValue() {
+ return this.value;
+ }
+ }
+
+ protected byte opCode;
+ protected byte hardwareType;
+ protected byte hardwareAddressLength;
+ protected byte hops;
+ protected int transactionId;
+ protected short seconds;
+ protected short flags;
+ protected int clientIPAddress;
+ protected int yourIPAddress;
+ protected int serverIPAddress;
+ protected int gatewayIPAddress;
+ protected byte[] clientHardwareAddress;
+ protected String serverName;
+ protected String bootFileName;
+ protected List<DHCPOption> options = new ArrayList<DHCPOption>();
+
+ /**
+ * @return the opCode
+ */
+ public byte getOpCode() {
+ return this.opCode;
+ }
+
+ /**
+ * @param opCode
+ * the opCode to set
+ * @return this
+ */
+ public DHCP setOpCode(final byte opCode) {
+ this.opCode = opCode;
+ return this;
+ }
+
+ /**
+ * @return the hardwareType
+ */
+ public byte getHardwareType() {
+ return this.hardwareType;
+ }
+
+ /**
+ * @param hardwareType
+ * the hardwareType to set
+ * @return this
+ */
+ public DHCP setHardwareType(final byte hardwareType) {
+ this.hardwareType = hardwareType;
+ return this;
+ }
+
+ /**
+ * @return the hardwareAddressLength
+ */
+ public byte getHardwareAddressLength() {
+ return this.hardwareAddressLength;
+ }
+
+ /**
+ * @param hardwareAddressLength
+ * the hardwareAddressLength to set
+ * @return this
+ */
+ public DHCP setHardwareAddressLength(final byte hardwareAddressLength) {
+ this.hardwareAddressLength = hardwareAddressLength;
+ return this;
+ }
+
+ /**
+ * @return the hops
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @param hops
+ * the hops to set
+ * @return this
+ */
+ public DHCP setHops(final byte hops) {
+ this.hops = hops;
+ return this;
+ }
+
+ /**
+ * @return the transactionId
+ */
+ public int getTransactionId() {
+ return this.transactionId;
+ }
+
+ /**
+ * @param transactionId
+ * the transactionId to set
+ * @return this
+ */
+ public DHCP setTransactionId(final int transactionId) {
+ this.transactionId = transactionId;
+ return this;
+ }
+
+ /**
+ * @return the seconds
+ */
+ public short getSeconds() {
+ return this.seconds;
+ }
+
+ /**
+ * @param seconds
+ * the seconds to set
+ * @return this
+ */
+ public DHCP setSeconds(final short seconds) {
+ this.seconds = seconds;
+ return this;
+ }
+
+ /**
+ * @return the flags
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @param flags
+ * the flags to set
+ * @return this
+ */
+ public DHCP setFlags(final short flags) {
+ this.flags = flags;
+ return this;
+ }
+
+ /**
+ * @return the clientIPAddress
+ */
+ public int getClientIPAddress() {
+ return this.clientIPAddress;
+ }
+
+ /**
+ * @param clientIPAddress
+ * the clientIPAddress to set
+ * @return this
+ */
+ public DHCP setClientIPAddress(final int clientIPAddress) {
+ this.clientIPAddress = clientIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the yourIPAddress
+ */
+ public int getYourIPAddress() {
+ return this.yourIPAddress;
+ }
+
+ /**
+ * @param yourIPAddress
+ * the yourIPAddress to set
+ * @return this
+ */
+ public DHCP setYourIPAddress(final int yourIPAddress) {
+ this.yourIPAddress = yourIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the serverIPAddress
+ */
+ public int getServerIPAddress() {
+ return this.serverIPAddress;
+ }
+
+ /**
+ * @param serverIPAddress
+ * the serverIPAddress to set
+ * @return this
+ */
+ public DHCP setServerIPAddress(final int serverIPAddress) {
+ this.serverIPAddress = serverIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the gatewayIPAddress
+ */
+ public int getGatewayIPAddress() {
+ return this.gatewayIPAddress;
+ }
+
+ /**
+ * @param gatewayIPAddress
+ * the gatewayIPAddress to set
+ * @return this
+ */
+ public DHCP setGatewayIPAddress(final int gatewayIPAddress) {
+ this.gatewayIPAddress = gatewayIPAddress;
+ return this;
+ }
+
+ /**
+ * @return the clientHardwareAddress
+ */
+ public byte[] getClientHardwareAddress() {
+ return this.clientHardwareAddress;
+ }
+
+ /**
+ * @param clientHardwareAddress
+ * the clientHardwareAddress to set
+ * @return this
+ */
+ public DHCP setClientHardwareAddress(final byte[] clientHardwareAddress) {
+ this.clientHardwareAddress = clientHardwareAddress;
+ return this;
+ }
+
+ /**
+ * Gets a specific DHCP option parameter.
+ *
+ * @param optionCode
+ * The option code to get
+ * @return The value of the option if it exists, null otherwise
+ */
+ public DHCPOption getOption(final DHCPOptionCode optionCode) {
+ for (final DHCPOption opt : this.options) {
+ if (opt.code == optionCode.value) {
+ return opt;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the options
+ */
+ public List<DHCPOption> getOptions() {
+ return this.options;
+ }
+
+ /**
+ * @param options
+ * the options to set
+ * @return this
+ */
+ public DHCP setOptions(final List<DHCPOption> options) {
+ this.options = options;
+ return this;
+ }
+
+ /**
+ * @return the packetType base on option 53
+ */
+ public DHCPPacketType getPacketType() {
+ final ListIterator<DHCPOption> lit = this.options.listIterator();
+ while (lit.hasNext()) {
+ final DHCPOption option = lit.next();
+ // only care option 53
+ if (option.getCode() == 53) {
+ return DHCPPacketType.getType(option.getData()[0]);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return the serverName
+ */
+ public String getServerName() {
+ return this.serverName;
+ }
+
+ /**
+ * @param server
+ * the serverName to set
+ * @return this
+ */
+ public DHCP setServerName(final String server) {
+ this.serverName = server;
+ return this;
+ }
+
+ /**
+ * @return the bootFileName
+ */
+ public String getBootFileName() {
+ return this.bootFileName;
+ }
+
+ /**
+ * @param bootFile
+ * the bootFileName to set
+ * @return this
+ */
+ public DHCP setBootFileName(final String bootFile) {
+ this.bootFileName = bootFile;
+ return this;
+ }
+
+ @Override
+ public byte[] serialize() {
+ // not guaranteed to retain length/exact format
+ this.resetChecksum();
+
+ // minimum size 240 including magic cookie, options generally padded to
+ // 300
+ int optionsLength = 0;
+ for (final DHCPOption option : this.options) {
+ if (option.getCode() == 0 || option.getCode() == ((byte) 255)) {
+ optionsLength += 1;
+ } else {
+ optionsLength += 2 + (0xff & option.getLength());
+ }
+ }
+ int optionsPadLength = 0;
+ if (optionsLength < 60) {
+ optionsPadLength = 60 - optionsLength;
+ }
+
+ final byte[] data = new byte[240 + optionsLength + optionsPadLength];
+ final ByteBuffer bb = ByteBuffer.wrap(data);
+ bb.put(this.opCode);
+ bb.put(this.hardwareType);
+ bb.put(this.hardwareAddressLength);
+ bb.put(this.hops);
+ bb.putInt(this.transactionId);
+ bb.putShort(this.seconds);
+ bb.putShort(this.flags);
+ bb.putInt(this.clientIPAddress);
+ bb.putInt(this.yourIPAddress);
+ bb.putInt(this.serverIPAddress);
+ bb.putInt(this.gatewayIPAddress);
+ checkArgument(this.clientHardwareAddress.length <= 16,
+ "Hardware address is too long (%s bytes)", this.clientHardwareAddress.length);
+ bb.put(this.clientHardwareAddress);
+ if (this.clientHardwareAddress.length < 16) {
+ for (int i = 0; i < 16 - this.clientHardwareAddress.length; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ this.writeString(this.serverName, bb, 64);
+ this.writeString(this.bootFileName, bb, 128);
+ // magic cookie
+ bb.put((byte) 0x63);
+ bb.put((byte) 0x82);
+ bb.put((byte) 0x53);
+ bb.put((byte) 0x63);
+ for (final DHCPOption option : this.options) {
+ final int code = option.getCode() & 0xff;
+ bb.put((byte) code);
+ if (code != 0 && code != 255) {
+ bb.put(option.getLength());
+ bb.put(option.getData());
+ }
+ }
+ // assume the rest is padded out with zeroes
+ return data;
+ }
+
+ @Override
+ public IPacket deserialize(final byte[] data, final int offset,
+ final int length) {
+ final ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ if (bb.remaining() < DHCP.MIN_HEADER_LENGTH) {
+ return this;
+ }
+
+ this.opCode = bb.get();
+ this.hardwareType = bb.get();
+ this.hardwareAddressLength = bb.get();
+ this.hops = bb.get();
+ this.transactionId = bb.getInt();
+ this.seconds = bb.getShort();
+ this.flags = bb.getShort();
+ this.clientIPAddress = bb.getInt();
+ this.yourIPAddress = bb.getInt();
+ this.serverIPAddress = bb.getInt();
+ this.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & this.hardwareAddressLength;
+ this.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(this.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ this.serverName = this.readString(bb, 64);
+ this.bootFileName = this.readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+ // read options
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ } else {
+ // Skip the invalid option and set the END option
+ code = 0xff;
+ option.setCode((byte) code);
+ option.setLength((byte) 0);
+ }
+ }
+ this.options.add(option);
+ if (code == 255) {
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ break;
+ }
+ }
+
+ return this;
+ }
+
+ protected void writeString(final String string, final ByteBuffer bb,
+ final int maxLength) {
+ if (string == null) {
+ for (int i = 0; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ } else {
+ byte[] bytes = null;
+ try {
+ bytes = string.getBytes("ascii");
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure encoding server name", e);
+ }
+ int writeLength = bytes.length;
+ if (writeLength > maxLength) {
+ writeLength = maxLength;
+ }
+ bb.put(bytes, 0, writeLength);
+ for (int i = writeLength; i < maxLength; ++i) {
+ bb.put((byte) 0x0);
+ }
+ }
+ }
+
+ private static String readString(final ByteBuffer bb, final int maxLength) {
+ final byte[] bytes = new byte[maxLength];
+ bb.get(bytes);
+ String result = null;
+ try {
+ result = new String(bytes, "ascii").trim();
+ } catch (final UnsupportedEncodingException e) {
+ throw new RuntimeException("Failure decoding string", e);
+ }
+ return result;
+ }
+
+ /**
+ * Deserializer function for DHCP packets.
+ *
+ * @return deserializer function
+ */
+ public static Deserializer<DHCP> deserializer() {
+ return (data, offset, length) -> {
+ checkInput(data, offset, length, MIN_HEADER_LENGTH);
+
+ ByteBuffer bb = ByteBuffer.wrap(data, offset, length);
+ DHCP dhcp = new DHCP();
+
+ dhcp.opCode = bb.get();
+ dhcp.hardwareType = bb.get();
+ dhcp.hardwareAddressLength = bb.get();
+ dhcp.hops = bb.get();
+ dhcp.transactionId = bb.getInt();
+ dhcp.seconds = bb.getShort();
+ dhcp.flags = bb.getShort();
+ dhcp.clientIPAddress = bb.getInt();
+ dhcp.yourIPAddress = bb.getInt();
+ dhcp.serverIPAddress = bb.getInt();
+ dhcp.gatewayIPAddress = bb.getInt();
+ final int hardwareAddressLength = 0xff & dhcp.hardwareAddressLength;
+ dhcp.clientHardwareAddress = new byte[hardwareAddressLength];
+
+ bb.get(dhcp.clientHardwareAddress);
+ for (int i = hardwareAddressLength; i < 16; ++i) {
+ bb.get();
+ }
+ dhcp.serverName = readString(bb, 64);
+ dhcp.bootFileName = readString(bb, 128);
+ // read the magic cookie
+ // magic cookie
+ bb.get();
+ bb.get();
+ bb.get();
+ bb.get();
+
+ // read options
+ boolean foundEndOptionsMarker = false;
+ while (bb.hasRemaining()) {
+ final DHCPOption option = new DHCPOption();
+ int code = 0xff & bb.get(); // convert signed byte to int in range
+ // [0,255]
+ option.setCode((byte) code);
+ if (code == 0) {
+ // skip these
+ continue;
+ } else if (code != 255) {
+ if (bb.hasRemaining()) {
+ final int l = 0xff & bb.get(); // convert signed byte to
+ // int in range [0,255]
+ option.setLength((byte) l);
+ if (bb.remaining() >= l) {
+ final byte[] optionData = new byte[l];
+ bb.get(optionData);
+ option.setData(optionData);
+ dhcp.options.add(option);
+ } else {
+ throw new DeserializationException(
+ "Buffer underflow while reading DHCP option");
+ }
+ }
+ } else if (code == 255) {
+ DHCPOption end = new DHCPOption();
+ end.setCode((byte) 255);
+ dhcp.options.add(end);
+ // remaining bytes are supposed to be 0, but ignore them just in
+ // case
+ foundEndOptionsMarker = true;
+ break;
+ }
+ }
+
+ if (!foundEndOptionsMarker) {
+ throw new DeserializationException("DHCP End options marker was missing");
+ }
+
+ return dhcp;
+ };
+ }
+}