org.pcap4j.packet.Dot11LinkAdaptationControl Maven / Gradle / Ivy
/*_##########################################################################
_##
_## Copyright (C) 2016 Pcap4J.org
_##
_##########################################################################
*/
package org.pcap4j.packet;
import java.io.Serializable;
import org.pcap4j.util.ByteArrays;
/**
* Link Adaptation Control field of an IEEE802.11 frame.
*
* {@code
* 0 1 2 3 4 5 6 7
* +--------+--------+--------+--------+--------+--------+--------+--------+
* |VHT_MFB | TRQ | MAI | MFSI |
* +--------+--------+--------+--------+--------+--------+--------+--------+
* | MFSI | MFB/ASELC |
* +--------+--------+--------+--------+--------+--------+--------+--------+
* }
*
* @see IEEE802.11
* @author Kaito Yamada
* @since pcap4j 1.7.0
*/
public final class Dot11LinkAdaptationControl implements Serializable {
/** */
private static final long serialVersionUID = 7735461000002622072L;
/** ASELI */
public static final byte ASELI = 14;
private final boolean vhtMfb;
private final boolean trq;
private final boolean aseli;
private final Mai mai;
private final byte mfsi;
private final Byte mfb;
private final Aselc aselc;
/**
* A static factory method. This method validates the arguments by {@link
* ByteArrays#validateBounds(byte[], int, int)}, which may throw exceptions undocumented here.
*
* @param rawData rawData
* @param offset offset
* @param length length
* @return a new Dot11LinkAdaptationControl object.
* @throws IllegalRawDataException if parsing the raw data fails.
*/
public static Dot11LinkAdaptationControl newInstance(byte[] rawData, int offset, int length)
throws IllegalRawDataException {
ByteArrays.validateBounds(rawData, offset, length);
return new Dot11LinkAdaptationControl(rawData, offset, length);
}
private Dot11LinkAdaptationControl(byte[] rawData, int offset, int length)
throws IllegalRawDataException {
if (length < 2) {
StringBuilder sb = new StringBuilder(200);
sb.append("The data is too short to build a Dot11LinkAdaptationControl (")
.append(2)
.append(" bytes). data: ")
.append(ByteArrays.toHexString(rawData, " "))
.append(", offset: ")
.append(offset)
.append(", length: ")
.append(length);
throw new IllegalRawDataException(sb.toString());
}
byte first = rawData[offset];
byte second = rawData[offset + 1];
this.vhtMfb = (first & 0x01) != 0;
this.trq = (first & 0x02) != 0;
this.aseli = ((first >> 2) & 0x0F) == ASELI;
if (aseli) {
this.mai = null;
} else {
this.mai = new Mai((first & 0x04) != 0, (byte) ((first >> 3) & 0x07));
}
this.mfsi = (byte) (((first >> 6) & 0x03) | ((second & 0x01) << 2));
byte mfbAselc = (byte) ((second >> 1) & 0x7F);
this.mfb = mfbAselc;
this.aselc = new Aselc(mfbAselc);
}
private Dot11LinkAdaptationControl(Builder builder) {
if (builder == null) {
throw new NullPointerException("builder is null.");
}
if ((builder.maiOrAseli & 0xF0) != 0) {
throw new IllegalArgumentException(
"(builder.maiOrAseli & 0xF0) must be zero. builder.maiOrAseli: " + builder.maiOrAseli);
}
if ((builder.mfsi & 0xF8) != 0) {
throw new IllegalArgumentException(
"(builder.mfsi & 0xF8) must be zero. builder.mfsi: " + builder.mfsi);
}
if ((builder.mfbOrAselc & 0x80) != 0) {
throw new IllegalArgumentException(
"(builder.mfbOrAselc & 0x80) must be zero. builder.mfbOrAselc: " + builder.mfbOrAselc);
}
this.vhtMfb = builder.vhtMfb;
this.trq = builder.trq;
this.aseli = builder.maiOrAseli == ASELI;
if (aseli) {
this.mai = null;
} else {
this.mai = new Mai(builder.maiOrAseli);
}
this.mfsi = builder.mfsi;
this.mfb = builder.mfbOrAselc;
this.aselc = new Aselc(builder.mfbOrAselc);
}
/** @return true if the VHT_MFB field is set to 1; false otherwise. */
public boolean isVhtMfb() {
return vhtMfb;
}
/** @return true if the TRQ field is set to 1; false otherwise. */
public boolean isTrq() {
return trq;
}
/**
* @return true if the MAI field is set to 14 (ASELI); false otherwise.
* @see #ASELI
*/
public boolean isAselIndicated() {
return aseli;
}
/**
* @return a {@link Mai} object if {@link #isAselIndicated() isAselIndicated} returns false;
* otherwise null.
*/
public Mai getMai() {
return mai;
}
/** @return mfsi */
public byte getMfsi() {
return mfsi;
}
/** @return mfsi */
public int getMfsiAsInt() {
return mfsi;
}
/**
* @return the value of MFB if {@link #isAselIndicated() isAselIndicated} returns false; otherwise
* null.
*/
public Byte getMfb() {
if (aseli) {
return null;
} else {
return mfb;
}
}
/**
* @return the value of MFB if {@link #isAselIndicated() isAselIndicated} returns false; otherwise
* null.
*/
public Integer getMfbAsInteger() {
if (aseli) {
return null;
} else {
return (int) mfb;
}
}
/**
* @return an {@link Aselc} object if {@link #isAselIndicated() isAselIndicated} returns true;
* otherwise null.
*/
public Aselc getAselc() {
if (aseli) {
return aselc;
} else {
return null;
}
}
/** @return a new Builder object populated with this object's fields. */
public Builder getBuilder() {
return new Builder(this);
}
/** @return the raw data. */
public byte[] getRawData() {
byte[] data = new byte[2];
byte maiOrAseli = aseli ? ASELI : mai.getRawData();
data[0] = (byte) ((mfsi << 6) | maiOrAseli << 2);
if (trq) {
data[0] |= 0x02;
}
if (vhtMfb) {
data[0] |= 0x01;
}
data[1] = (byte) (mfb << 1);
if ((mfsi & 0x04) != 0) {
data[1] |= 0x01;
}
return data;
}
/** @return length */
public int length() {
return 2;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(250);
sb.append("[VHT_MFB: ")
.append(vhtMfb)
.append(", TRQ: ")
.append(trq)
.append(", ASELI: ")
.append(aseli);
if (!aseli) {
sb.append(", MAI: ").append(mai);
}
sb.append(", MFSI: ").append(mfsi);
if (aseli) {
sb.append(", ASELC: ").append(aselc);
} else {
sb.append(", MFB: ").append(mfb);
}
sb.append("]");
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((mai == null) ? 0 : mai.hashCode());
result = prime * result + mfb.hashCode();
result = prime * result + mfsi;
result = prime * result + (aseli ? 1231 : 1237);
result = prime * result + (vhtMfb ? 1231 : 1237);
result = prime * result + (trq ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Dot11LinkAdaptationControl other = (Dot11LinkAdaptationControl) obj;
if (mai == null) {
if (other.mai != null) return false;
} else if (!mai.equals(other.mai)) return false;
if (!mfb.equals(other.mfb)) return false;
if (mfsi != other.mfsi) return false;
if (aseli != other.aseli) return false;
if (vhtMfb != other.vhtMfb) return false;
if (trq != other.trq) return false;
return true;
}
/**
* MAI subfield
*
*
* 0 1 2 3
* +---+---+---+---+
* |MRQ| MSI |
* +---+---+---+---+
*
*
* @author Kaito Yamada
* @since pcap4j 1.7.0
*/
public static final class Mai implements Serializable {
/** */
private static final long serialVersionUID = -7417614720576047794L;
private final boolean mrq;
private final byte msi;
/**
* @param mrq mrq
* @param msi msi
*/
public Mai(boolean mrq, byte msi) {
if (msi < 0 || msi > 6) {
throw new IllegalArgumentException("msi must be between 0 and 6 but is actually: " + msi);
}
this.mrq = mrq;
this.msi = msi;
}
/** @param rawData the raw data which the MRQ is encoded at the LSB. */
public Mai(byte rawData) {
this.mrq = (rawData & 0x01) != 0;
this.msi = (byte) ((rawData >> 1) & 0x07);
}
/** @return mrq */
public boolean isMrq() {
return mrq;
}
/** @return msi */
public byte getMsi() {
return msi;
}
/** @return the raw data */
public byte getRawData() {
if (mrq) {
return (byte) ((msi << 1) | 1);
} else {
return (byte) (msi << 1);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(250);
sb.append("[MRQ: ").append(mrq).append(", MSI: ").append(msi).append("]");
return sb.toString();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (mrq ? 1231 : 1237);
result = prime * result + msi;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Mai other = (Mai) obj;
if (mrq != other.mrq) return false;
if (msi != other.msi) return false;
return true;
}
}
/**
* ASELC subfield
*
*
* 0 1 2 3 4 5
* +----+----+----+----+----+----+----+
* | ASEL Command | ASEL Data |
* +----+----+----+----+----+----+----+
*
*
* @author Kaito Yamada
* @since pcap4j 1.7.0
*/
public static final class Aselc implements Serializable {
/** */
private static final long serialVersionUID = -5404846090809709793L;
private final AselCommand command;
private final byte data;
/**
* @param command command
* @param data data
*/
public Aselc(AselCommand command, byte data) {
if (command == null) {
throw new IllegalArgumentException("command is null.");
}
if ((data & 0xF0) != 0) {
throw new IllegalArgumentException("(data & 0xF0) must be zero. data: " + data);
}
this.command = command;
this.data = data;
}
/** @param rawData the raw data which the ASEL Command is encoded in 3 bits from the LSB. */
public Aselc(byte rawData) {
this.command = AselCommand.getInstance(rawData & 0x07);
this.data = (byte) ((rawData >> 3) & 0x0F);
}
/** @return command */
public AselCommand getCommand() {
return command;
}
/** @return data */
public byte getData() {
return data;
}
/** @return the raw data. */
public byte getRawData() {
return (byte) ((command.value << 4) | (data));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + command.hashCode();
result = prime * result + data;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Aselc other = (Aselc) obj;
if (command != other.command) return false;
if (data != other.data) return false;
return true;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(250);
sb.append("[ASEL Command: ").append(command).append(", ASEL Data: ").append(data).append("]");
return sb.toString();
}
}
/**
* @author Kaito Yamada
* @since pcap4j 1.7.0
*/
public static enum AselCommand {
/** Transmit Antenna Selection Sounding Indication (TXASSI): 0 */
TXASSI(0, "TXASSI"),
/**
* Transmit Antenna Selection Sounding Request (TXASSR) or Transmit ASEL Sounding Resumption: 1
*/
TXASSR(1, "TXASSR"),
/** Receive Antenna Selection Sounding Indication (RXASSI): 2 */
RXASSI(2, "RXASSI"),
/** Receive Antenna Selection Sounding Request (RXASSR): 3 */
RXASSR(3, "RXASSR"),
/** Sounding Label: 4 */
SOUNDING_LABEL(4, "Sounding Label"),
/** No Feedback Due to ASEL Training Failure or Stale Feedback: 5 */
NO_FEEDBACK(5, "No Feedback"),
/**
* Transmit Antenna Selection Sounding Indication requesting feedback of explicit CSI
* (TXASSI-CSI): 6
*/
TXASSI_CSI(6, "TXASSI-CSI"),
/** Reserved: 7 */
SEVEN(7, "Reserved");
private final int value;
private final String name;
private AselCommand(int value, String name) {
this.value = value;
this.name = name;
}
/** @return value */
public int getValue() {
return value;
}
/** @return name */
public String getName() {
return name;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(50);
sb.append(value).append(" (").append(name).append(")");
return sb.toString();
}
/**
* @param value value
* @return the AselCommand object the value of which is the given value.
*/
public static AselCommand getInstance(int value) {
for (AselCommand com : values()) {
if (com.value == value) {
return com;
}
}
throw new IllegalArgumentException("Invalid value: " + value);
}
}
/**
* @author Kaito Yamada
* @since pcap4j 1.7.0
*/
public static final class Builder {
private boolean vhtMfb;
private boolean trq;
private byte maiOrAseli;
private byte mfsi;
private byte mfbOrAselc;
/** */
public Builder() {}
private Builder(Dot11LinkAdaptationControl obj) {
this.vhtMfb = obj.vhtMfb;
this.trq = obj.trq;
this.maiOrAseli = obj.mai.getRawData();
this.mfsi = obj.mfsi;
this.mfbOrAselc = obj.mfb;
}
/**
* @param vhtMfb vhtMfb
* @return this Builder object for method chaining.
*/
public Builder vhtMfb(boolean vhtMfb) {
this.vhtMfb = vhtMfb;
return this;
}
/**
* @param trq trq
* @return this Builder object for method chaining.
*/
public Builder trq(boolean trq) {
this.trq = trq;
return this;
}
/**
* @param maiOrAseli maiOrAseli
* @return this Builder object for method chaining.
* @see #ASELI
*/
public Builder maiOrAseli(byte maiOrAseli) {
this.maiOrAseli = maiOrAseli;
return this;
}
/**
* @param maiOrAseli maiOrAseli
* @return this Builder object for method chaining.
*/
public Builder maiOrAseli(Mai maiOrAseli) {
this.maiOrAseli = maiOrAseli.getRawData();
return this;
}
/**
* @param mfsi mfsi
* @return this Builder object for method chaining.
*/
public Builder mfsi(byte mfsi) {
this.mfsi = mfsi;
return this;
}
/**
* @param mfbOrAselc mfbOrAselc
* @return this Builder object for method chaining.
*/
public Builder mfbOrAselc(byte mfbOrAselc) {
this.mfbOrAselc = mfbOrAselc;
return this;
}
/**
* @param mfbOrAselc mfbOrAselc
* @return this Builder object for method chaining.
*/
public Builder mfbOrAselc(Aselc mfbOrAselc) {
this.mfbOrAselc = mfbOrAselc.getRawData();
return this;
}
/** @return a new Dot11LinkAdaptationControl object. */
public Dot11LinkAdaptationControl build() {
return new Dot11LinkAdaptationControl(this);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy