Mon, Oct 12, 2015

Step by Step to Add a Protocol Support to Pcap4J (Part 2)

Step by Step to Add a Protocol Support to Pcap4J (Part 2)

This is continued from the part 1.

We are adding DHCP support to Pcap4J.


Packet Piece Class

A packet piece class is a Java class which represents a field of a packet. We should create such classes instead of using a primitive types in some cases.

In the case of DHCP, its flags field includes two fields in itself as like below:

                    1 1 1 1 1 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|B|             MBZ             |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

B:  BROADCAST flag
MBZ:  MUST BE ZERO (reserved for future use)

Although this flags field is 2 bytes long and can be held by a primitive short variable, it’s better to create a packet piece class to hold it for better usability.

I mean,

boolean broadcast = DhcpV4Packet.getHeader().getFlags().isBroadcast();

is better than

boolean broadcast = (0x8000 & DhcpV4Packet.getHeader().getFlags()) != 0;

DhcpV4Flags class

Now, let’s write a packet piece class DhcpV4Flags for the flags field. For writing packet piece classes, there is no rule except that they must implement Serializable interface.

package org.pcap4j.packet;

import java.io.Serializable;

public final class DhcpV4Flags implements Serializable {

  private static final long serialVersionUID = -7144264525666462708L;

  private final short value;

  public static DhcpV4Flags newInstance(short value) {
    return new DhcpV4Flags(value);
  }

  private DhcpV4Flags(short value) {
    this.value = value;
  }

  public short value() {
    return value;
  }

}

What we should do to implement Serializable is only adding a field private static final long serialVersionUID in this case because DhcpV4Flags has only one primitive short field.

The short field holds entire value of the flags field. The constructor receives a short value and simply stores it in the short field. I made the constructor private and wrote a static factory method newInstance, which is just my taste and not necessary.


Here, let’s remember that the flags field has two fields in itself, which are B (BROADCAST) and MBZ (MUST BE ZERO), and add two methods to get them.

  public boolean isBroadcast() { return (value & 0x8000) != 0; }

  public short getMbz() { return (short)(value & 0x7FFF); }


And, generally speaking, we should always override toString, equals, and hashCode.

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("[BROADCAST: ")
      .append(isBroadcast())
      .append("] [value: 0x")
      .append(ByteArrays.toHexString(value, ""))
      .append("]");

    return sb.toString();
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) { return true; }
    if (!this.getClass().isInstance(obj)) { return false; }

    DhcpV4Flags other = (DhcpV4Flags)obj;
    return this.value == other.value;
  }

  @Override
  public int hashCode() { return value; }


The last thing to write is Builder class, which is another way to instantiate DhcpV4Flags objects. The static factory method I wrote above is used when dissecting a real DHCP packet (i.e. a byte array), while the Builder is used when crafting a DHCP packet object.

A Builder class is usually written as an inner class.

public final class DhcpV4Flags implements Serializable {

  (snip)

  public Builder getBuilder() {
    return new Builder(this);
  }

  public static final class Builder {

    private boolean broadcast = false;
    private short mbz = 0;

    public Builder() {}

    private Builder(DhcpV4Flags flags) {
      this.broadcast = flags.isBroadcast();
      this.mbz = flags.getMbz();
    }

    public Builder broadcast(boolean broadcast) {
      this.broadcast = broadcast;
      return this;
    }

    public Builder mbz(short mbz) {
      this.mbz = mbz;
      return this;
    }

    public DhcpV4Flags build() {
      return new DhcpV4Flags(this);
    }

  }

}

And, DhcpV4Flags needs a constructor which uses the Builder.

  private DhcpV4Flags(Builder builder) {
    if (builder == null) {
      throw new NullPointerException("builder is null.");
    }
    if (builder.mbz < 0) {
      throw new IllegalArgumentException(
              "mbz must be equal or greater than zero but it is: " + builder.mbz
            );
    }

    this.value = builder.broadcast ? (short)(builder.mbz | 0x8000) : builder.mbz;
  }


That’s all. The entire code is as follows:

package org.pcap4j.packet;

import java.io.Serializable;
import org.pcap4j.util.ByteArrays;

public final class DhcpV4Flags implements Serializable {

  private static final long serialVersionUID = -7144264525666462708L;

  private final short value;

  public static DhcpV4Flags newInstance(short value) {
    return new DhcpV4Flags(value);
  }

  private DhcpV4Flags(short value) {
    this.value = value;
  }

  private DhcpV4Flags(Builder builder) {
    if (builder == null) {
      throw new NullPointerException("builder is null.");
    }
    if (builder.mbz < 0) {
      throw new IllegalArgumentException(
              "mbz must be equal or greater than zero but it is: " + builder.mbz
            );
    }

    this.value = builder.broadcast ? (short)(builder.mbz | 0x8000) : builder.mbz;
  }

  public short value() {
    return value;
  }

  public boolean isBroadcast() { return (value & 0x8000) != 0; }

  public short getMbz() { return (short)(value & 0x7FFF); }

  public Builder getBuilder() {
    return new Builder(this);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("[BROADCAST: ")
      .append(isBroadcast())
      .append("] [value: 0x")
      .append(ByteArrays.toHexString(value, ""))
      .append("]");

    return sb.toString();
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) { return true; }
    if (!this.getClass().isInstance(obj)) { return false; }

    DhcpV4Flags other = (DhcpV4Flags)obj;
    return this.value == other.value;
  }

  @Override
  public int hashCode() { return value; }

  public static final class Builder {

    private boolean broadcast = false;
    private short mbz = 0;

    public Builder() {}

    private Builder(DhcpV4Flags flags) {
      this.broadcast = flags.isBroadcast();
      this.mbz = flags.getMbz();
    }

    public Builder broadcast(boolean broadcast) {
      this.broadcast = broadcast;
      return this;
    }

    public Builder mbz(short mbz) {
      this.mbz = mbz;
      return this;
    }

    public DhcpV4Flags build() {
      return new DhcpV4Flags(this);
    }

  }

}


In the next part, we’ll write the DHCP packet class.