private UnknownIpV6NeighborDiscoveryOption(byte[] rawData, int offset, int length)
      throws IllegalRawDataException {
    if (length < 2) {
      StringBuilder sb = new StringBuilder(100);
      sb.append("The raw data length must be more than 1. rawData: ")
          .append(ByteArrays.toHexString(rawData, " "))
          .append(", offset: ")
          .append(offset)
          .append(", length: ")
          .append(length);
      throw new IllegalRawDataException(sb.toString());
    }

    this.type = IpV6NeighborDiscoveryOptionType.getInstance(rawData[offset]);
    this.length = rawData[1 + offset];
    if (length < this.length * 8) {
      StringBuilder sb = new StringBuilder(100);
      sb.append("The raw data is too short to build this option(")
          .append(this.length * 8)
          .append("). data: ")
          .append(ByteArrays.toHexString(rawData, " "))
          .append(", offset: ")
          .append(offset)
          .append(", length: ")
          .append(length);
      throw new IllegalRawDataException(sb.toString());
    }

    this.data = ByteArrays.getSubArray(rawData, 2 + offset, this.length * 8 - 2);
  }
 @Override
 public int hashCode() {
   int result = 17;
   result = 31 * result + type.hashCode();
   result = 31 * result + length;
   result = 31 * result + Arrays.hashCode(data);
   return result;
 }
 @Override
 public byte[] getRawData() {
   byte[] rawData = new byte[length()];
   rawData[0] = type.value();
   rawData[1] = length;
   System.arraycopy(data, 0, rawData, 2, data.length);
   return rawData;
 }
  @Override
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    }
    if (!this.getClass().isInstance(obj)) {
      return false;
    }

    UnknownIpV6NeighborDiscoveryOption other = (UnknownIpV6NeighborDiscoveryOption) obj;
    return type.equals(other.type) && length == other.length && Arrays.equals(data, other.data);
  }
    private IcmpV6NeighborAdvertisementHeader(byte[] rawData, int offset, int length)
        throws IllegalRawDataException {
      if (length < OPTIONS_OFFSET) {
        StringBuilder sb = new StringBuilder(120);
        sb.append("The raw data must be more than ")
            .append(OPTIONS_OFFSET - 1)
            .append("bytes")
            .append(" to build this header. raw data: ")
            .append(ByteArrays.toHexString(rawData, " "))
            .append(", offset: ")
            .append(offset)
            .append(", length: ")
            .append(length);
        throw new IllegalRawDataException(sb.toString());
      }

      int tmp = ByteArrays.getInt(rawData, R_S_O_RESERVED_OFFSET + offset);
      this.routerFlag = (tmp & 0x80000000) != 0;
      this.solicitedFlag = (tmp & 0x40000000) != 0;
      this.overrideFlag = (tmp & 0x20000000) != 0;
      this.reserved = 0x1FFFFFFF & tmp;
      this.targetAddress = ByteArrays.getInet6Address(rawData, TARGET_ADDRESS_OFFSET + offset);
      this.options = new ArrayList<IpV6NeighborDiscoveryOption>();
      int currentOffsetInHeader = OPTIONS_OFFSET;
      while (currentOffsetInHeader < length) {
        IpV6NeighborDiscoveryOptionType type =
            IpV6NeighborDiscoveryOptionType.getInstance(rawData[currentOffsetInHeader + offset]);
        IpV6NeighborDiscoveryOption newOne;
        try {
          newOne =
              PacketFactories.getFactory(
                      IpV6NeighborDiscoveryOption.class, IpV6NeighborDiscoveryOptionType.class)
                  .newInstance(
                      rawData,
                      currentOffsetInHeader + offset,
                      length - currentOffsetInHeader,
                      type);
        } catch (Exception e) {
          break;
        }

        options.add(newOne);
        currentOffsetInHeader += newOne.length();
      }
    }