[ONOS-4718] Initial implementation of LISP control msg deserializer
- Add LispLocatorRecord interface along with
DefaultLispLocatorRecord class and unit test class
- Add deserialization logic for four LISP control message classes
and two auxiliary classes
- Add ByteOperator utility to ease the bit access and manipulation
for byte data type
Change-Id: I68edf6877a0ebb52260296fc556e0690b795a845
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecord.java
new file mode 100644
index 0000000..29ad1bd
--- /dev/null
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecord.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2016-present 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.onosproject.lisp.msg.protocols;
+
+import com.google.common.base.Objects;
+import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
+import org.onosproject.lisp.msg.exceptions.LispParseError;
+import org.onosproject.lisp.msg.types.LispAfiAddress;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * Default implementation of LispLocatorRecord.
+ */
+public final class DefaultLispLocatorRecord implements LispLocatorRecord {
+
+ private final byte priority;
+ private final byte weight;
+ private final byte multicastPriority;
+ private final byte multicastWeight;
+ private final boolean localLocator;
+ private final boolean rlocProbed;
+ private final boolean routed;
+ private final LispAfiAddress locatorAfi;
+
+ /**
+ * A private constructor that protects object instantiation from external.
+ *
+ * @param priority uni-cast priority
+ * @param weight uni-cast weight
+ * @param multicastPriority multi-cast priority
+ * @param multicastWeight multi-cast weight
+ * @param localLocator local locator flag
+ * @param rlocProbed RLOC probed flag
+ * @param routed routed flag
+ * @param locatorAfi locator AFI
+ */
+ private DefaultLispLocatorRecord(byte priority, byte weight, byte multicastPriority,
+ byte multicastWeight, boolean localLocator, boolean rlocProbed,
+ boolean routed, LispAfiAddress locatorAfi) {
+ this.priority = priority;
+ this.weight = weight;
+ this.multicastPriority = multicastPriority;
+ this.multicastWeight = multicastWeight;
+ this.localLocator = localLocator;
+ this.rlocProbed = rlocProbed;
+ this.routed = routed;
+ this.locatorAfi = locatorAfi;
+ }
+
+ @Override
+ public byte getPriority() {
+ return priority;
+ }
+
+ @Override
+ public byte getWeight() {
+ return weight;
+ }
+
+ @Override
+ public byte getMulticastPriority() {
+ return multicastPriority;
+ }
+
+ @Override
+ public byte getMulticastWeight() {
+ return multicastWeight;
+ }
+
+ @Override
+ public boolean isLocalLocator() {
+ return localLocator;
+ }
+
+ @Override
+ public boolean isRlocProbed() {
+ return rlocProbed;
+ }
+
+ @Override
+ public boolean isRouted() {
+ return routed;
+ }
+
+ @Override
+ public LispAfiAddress getLocatorAfi() {
+ return locatorAfi;
+ }
+
+ @Override
+ public void writeTo(ByteBuf byteBuf) {
+
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("priority", priority)
+ .add("weight", weight)
+ .add("multi-cast priority", multicastPriority)
+ .add("multi-cast weight", multicastWeight)
+ .add("local locator", localLocator)
+ .add("RLOC probed", rlocProbed)
+ .add("routed", routed)
+ .add("locator AFI", locatorAfi).toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DefaultLispLocatorRecord that = (DefaultLispLocatorRecord) o;
+ return Objects.equal(priority, that.priority) &&
+ Objects.equal(weight, that.weight) &&
+ Objects.equal(multicastPriority, that.multicastPriority) &&
+ Objects.equal(multicastWeight, that.multicastWeight) &&
+ Objects.equal(localLocator, that.localLocator) &&
+ Objects.equal(rlocProbed, that.rlocProbed) &&
+ Objects.equal(routed, that.routed) &&
+ Objects.equal(locatorAfi, that.locatorAfi);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(priority, weight, multicastPriority,
+ multicastWeight, localLocator, rlocProbed, routed, locatorAfi);
+ }
+
+ public static final class DefaultLocatorRecordBuilder implements LocatorRecordBuilder {
+
+ private byte priority;
+ private byte weight;
+ private byte multicastPriority;
+ private byte multicastWeight;
+ private boolean localLocator;
+ private boolean rlocProbed;
+ private boolean routed;
+ private LispAfiAddress locatorAfi;
+
+ @Override
+ public LocatorRecordBuilder withPriority(byte priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withWeight(byte weight) {
+ this.weight = weight;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withMulticastPriority(byte priority) {
+ this.multicastPriority = priority;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withMulticastWeight(byte weight) {
+ this.multicastWeight = weight;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withLocalLocator(boolean localLocator) {
+ this.localLocator = localLocator;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withRlocProbed(boolean rlocProbed) {
+ this.rlocProbed = rlocProbed;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withRouted(boolean routed) {
+ this.routed = routed;
+ return this;
+ }
+
+ @Override
+ public LocatorRecordBuilder withLocatorAfi(LispAfiAddress locatorAfi) {
+ this.locatorAfi = locatorAfi;
+ return this;
+ }
+
+ @Override
+ public LispLocatorRecord build() {
+ return new DefaultLispLocatorRecord(priority, weight, multicastPriority,
+ multicastWeight, localLocator, rlocProbed, routed, locatorAfi);
+ }
+ }
+
+ /**
+ * A LISP message reader for LocatorRecord portion.
+ */
+ public static final class LocatorRecordReader implements LispMessageReader<LispLocatorRecord> {
+
+ private static final int SKIP_UNUSED_FLAG_LENGTH = 1;
+ private static final int LOCAL_LOCATOR_INDEX = 2;
+ private static final int RLOC_PROBED_INDEX = 1;
+ private static final int ROUTED_INDEX = 0;
+
+ @Override
+ public LispLocatorRecord readFrom(ByteBuf byteBuf) throws LispParseError {
+
+ // priority -> 8 bits
+ byte priority = (byte) byteBuf.readUnsignedByte();
+
+ // weight -> 8 bits
+ byte weight = (byte) byteBuf.readUnsignedByte();
+
+ // multi-cast priority -> 8 bits
+ byte multicastPriority = (byte) byteBuf.readUnsignedByte();
+
+ // multi-cast weight -> 8 bits
+ byte multicastWeight = (byte) byteBuf.readUnsignedByte();
+
+ // let's skip unused flags
+ byteBuf.skipBytes(SKIP_UNUSED_FLAG_LENGTH);
+
+ byte flags = byteBuf.readByte();
+
+ // local locator flag -> 1 bit
+ boolean localLocator = ByteOperator.getBit(flags, LOCAL_LOCATOR_INDEX);
+
+ // rloc probe flag -> 1 bit
+ boolean rlocProbed = ByteOperator.getBit(flags, RLOC_PROBED_INDEX);
+
+ // routed flag -> 1 bit
+ boolean routed = ByteOperator.getBit(flags, ROUTED_INDEX);
+
+ // TODO: de-serialize ITR-RLOC AFI and address
+
+ return new DefaultLocatorRecordBuilder()
+ .withPriority(priority)
+ .withWeight(weight)
+ .withMulticastPriority(multicastPriority)
+ .withMulticastWeight(multicastWeight)
+ .withLocalLocator(localLocator)
+ .withRlocProbed(rlocProbed)
+ .withRouted(routed)
+ .build();
+ }
+ }
+}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotify.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotify.java
index 831065a..c80b4d1 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotify.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotify.java
@@ -131,7 +131,7 @@
private short keyId;
private byte[] authenticationData;
private byte recordCount;
- private List<LispMapRecord> mapRecords = Lists.newArrayList();
+ private List<LispMapRecord> mapRecords;
@Override
public LispType getType() {
@@ -163,8 +163,8 @@
}
@Override
- public NotifyBuilder addRecord(LispMapRecord record) {
- this.mapRecords.add(record);
+ public NotifyBuilder withMapRecords(List<LispMapRecord> mapRecords) {
+ this.mapRecords = ImmutableList.copyOf(mapRecords);
return this;
}
@@ -180,9 +180,46 @@
*/
private static class NotifyReader implements LispMessageReader<LispMapNotify> {
+ private static final int RESERVED_SKIP_LENGTH = 3;
+
@Override
public LispMapNotify readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ if (byteBuf.readerIndex() != 0) {
+ return null;
+ }
+
+ // skip first three bytes as they represent type and reserved fields
+ byteBuf.skipBytes(RESERVED_SKIP_LENGTH);
+
+ // record count -> 8 bits
+ byte recordCount = (byte) byteBuf.readUnsignedByte();
+
+ // nonce -> 64 bits
+ long nonce = byteBuf.readLong();
+
+ // keyId -> 16 bits
+ short keyId = byteBuf.readShort();
+
+ // authenticationDataLength -> 16 bits
+ short authLength = byteBuf.readShort();
+
+ // authenticationData -> depends on the authenticationDataLength
+ byte[] authData = new byte[authLength];
+ byteBuf.readBytes(authData);
+
+ List<LispMapRecord> mapRecords = Lists.newArrayList();
+ for (int i = 0; i < recordCount; i++) {
+ mapRecords.add(new DefaultLispMapRecord.MapRecordReader().readFrom(byteBuf));
+ }
+
+ return new DefaultNotifyBuilder()
+ .withRecordCount(recordCount)
+ .withNonce(nonce)
+ .withKeyId(keyId)
+ .withAuthenticationData(authData)
+ .withMapRecords(mapRecords)
+ .build();
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecord.java
index 7b0c86c..c681e06 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecord.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecord.java
@@ -16,10 +16,15 @@
package org.onosproject.lisp.msg.protocols;
import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
import org.onosproject.lisp.msg.exceptions.LispParseError;
import org.onosproject.lisp.msg.types.LispAfiAddress;
+import java.util.List;
+
import static com.google.common.base.MoreObjects.toStringHelper;
/**
@@ -34,6 +39,7 @@
private final boolean authoritative;
private final short mapVersionNumber;
private final LispAfiAddress eidPrefixAfi;
+ private final List<LispLocatorRecord> locatorRecords;
/**
* A private constructor that protects object instantiation from external.
@@ -48,7 +54,8 @@
*/
private DefaultLispMapRecord(int recordTtl, int locatorCount, byte maskLength,
LispMapReplyAction action, boolean authoritative,
- short mapVersionNumber, LispAfiAddress eidPrefixAfi) {
+ short mapVersionNumber, LispAfiAddress eidPrefixAfi,
+ List<LispLocatorRecord> locatorRecords) {
this.recordTtl = recordTtl;
this.locatorCount = locatorCount;
this.maskLength = maskLength;
@@ -56,6 +63,7 @@
this.authoritative = authoritative;
this.mapVersionNumber = mapVersionNumber;
this.eidPrefixAfi = eidPrefixAfi;
+ this.locatorRecords = locatorRecords;
}
@Override
@@ -94,6 +102,16 @@
}
@Override
+ public List<LispLocatorRecord> getLocators() {
+ return ImmutableList.copyOf(locatorRecords);
+ }
+
+ @Override
+ public void writeTo(ByteBuf byteBuf) {
+
+ }
+
+ @Override
public String toString() {
return toStringHelper(this)
.add("record TTL", recordTtl)
@@ -102,7 +120,9 @@
.add("action", action)
.add("authoritative", authoritative)
.add("mapVersionNumber", mapVersionNumber)
- .add("EID prefix AFI address", eidPrefixAfi).toString();
+ .add("EID prefix AFI address", eidPrefixAfi)
+ .add("locator records", locatorRecords).toString();
+
}
@Override
@@ -120,13 +140,14 @@
Objects.equal(action, that.action) &&
Objects.equal(authoritative, that.authoritative) &&
Objects.equal(mapVersionNumber, that.mapVersionNumber) &&
- Objects.equal(eidPrefixAfi, that.eidPrefixAfi);
+ Objects.equal(eidPrefixAfi, that.eidPrefixAfi) &&
+ Objects.equal(locatorRecords, that.locatorRecords);
}
@Override
public int hashCode() {
return Objects.hashCode(recordTtl, locatorCount, maskLength, action,
- authoritative, mapVersionNumber, eidPrefixAfi);
+ authoritative, mapVersionNumber, eidPrefixAfi, locatorRecords);
}
public static final class DefaultMapRecordBuilder implements MapRecordBuilder {
@@ -138,6 +159,7 @@
private boolean authoritative;
private short mapVersionNumber;
private LispAfiAddress eidPrefixAfi;
+ private List<LispLocatorRecord> locatorRecords;
@Override
public MapRecordBuilder withRecordTtl(int recordTtl) {
@@ -182,20 +204,66 @@
}
@Override
+ public MapRecordBuilder withLocators(List<LispLocatorRecord> records) {
+ this.locatorRecords = ImmutableList.copyOf(records);
+ return this;
+ }
+
+ @Override
public LispMapRecord build() {
return new DefaultLispMapRecord(recordTtl, locatorCount, maskLength,
- action, authoritative, mapVersionNumber, eidPrefixAfi);
+ action, authoritative, mapVersionNumber, eidPrefixAfi, locatorRecords);
}
}
/**
- * A private LISP message reader for MapRecord portion.
+ * A LISP message reader for MapRecord portion.
*/
- private static class RecordReader implements LispMessageReader<LispMapRecord> {
+ public static final class MapRecordReader implements LispMessageReader<LispMapRecord> {
+
+ private static final int AUTHORITATIVE_INDEX = 4;
+ private static final int RESERVED_SKIP_LENGTH = 1;
@Override
public LispMapRecord readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ // Record TTL -> 32 bits
+ int recordTtl = byteBuf.readInt();
+
+ // Locator count -> 8 bits
+ int locatorCount = byteBuf.readUnsignedShort();
+
+ // EID mask length -> 8 bits
+ byte maskLength = (byte) byteBuf.readUnsignedByte();
+
+ // TODO: need to de-serialize LispMapReplyAction
+
+ byte actionWithFlag = byteBuf.readByte();
+
+ // authoritative flag -> 1 bit
+ boolean authoritative = ByteOperator.getBit(actionWithFlag, AUTHORITATIVE_INDEX);
+
+ // let's skip the reserved field
+ byteBuf.skipBytes(RESERVED_SKIP_LENGTH);
+
+ // Map version number -> 12 bits, we treat Rsvd field is all zero
+ short mapVersionNumber = (short) byteBuf.readUnsignedShort();
+
+ // TODO: need to de-serialize EID AFI address
+
+ List<LispLocatorRecord> locators = Lists.newArrayList();
+ for (int i = 0; i < locatorCount; i++) {
+ locators.add(new DefaultLispLocatorRecord.LocatorRecordReader().readFrom(byteBuf));
+ }
+
+ return new DefaultMapRecordBuilder()
+ .withRecordTtl(recordTtl)
+ .withLocatorCount(locatorCount)
+ .withMaskLength(maskLength)
+ .withAuthoritative(authoritative)
+ .withMapVersionNumber(mapVersionNumber)
+ .withLocators(locators)
+ .build();
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegister.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegister.java
index b77e580..058db10 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegister.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegister.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
import org.onlab.util.ImmutableByteSequence;
import org.onosproject.lisp.msg.exceptions.LispParseError;
@@ -155,7 +156,7 @@
private short keyId;
private byte[] authenticationData;
private byte recordCount;
- private final List<LispMapRecord> mapRecords = Lists.newArrayList();
+ private List<LispMapRecord> mapRecords;
private boolean proxyMapReply;
private boolean wantMapNotify;
@@ -201,8 +202,8 @@
}
@Override
- public RegisterBuilder addRecord(LispMapRecord record) {
- this.mapRecords.add(record);
+ public RegisterBuilder withMapRecords(List<LispMapRecord> mapRecords) {
+ this.mapRecords = ImmutableList.copyOf(mapRecords);
return this;
}
@@ -218,9 +219,58 @@
*/
private static class RegisterReader implements LispMessageReader<LispMapRegister> {
+ private static final int PROXY_MAP_REPLY_INDEX = 3;
+ private static final int WANT_MAP_NOTIFY_INDEX = 0;
+ private static final int RESERVED_SKIP_LENGTH = 1;
+
@Override
public LispMapRegister readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ if (byteBuf.readerIndex() != 0) {
+ return null;
+ }
+
+ // proxyMapReply -> 1 bit
+ boolean proxyMapReplyFlag = ByteOperator.getBit(byteBuf.readByte(), PROXY_MAP_REPLY_INDEX);
+
+ // let's skip the reserved field
+ byteBuf.skipBytes(RESERVED_SKIP_LENGTH);
+
+ byte reservedWithFlag = byteBuf.readByte();
+
+ // wantMapReply -> 1 bit
+ boolean wantMapNotifyFlag = ByteOperator.getBit(reservedWithFlag, WANT_MAP_NOTIFY_INDEX);
+
+ // record count -> 8 bits
+ byte recordCount = (byte) byteBuf.readUnsignedByte();
+
+ // nonce -> 64 bits
+ long nonce = byteBuf.readLong();
+
+ // keyId -> 16 bits
+ short keyId = byteBuf.readShort();
+
+ // authenticationDataLength -> 16 bits
+ short authLength = byteBuf.readShort();
+
+ // authenticationData -> depends on the authenticationDataLength
+ byte[] authData = new byte[authLength];
+ byteBuf.readBytes(authData);
+
+ List<LispMapRecord> mapRecords = Lists.newArrayList();
+ for (int i = 0; i < recordCount; i++) {
+ mapRecords.add(new DefaultLispMapRecord.MapRecordReader().readFrom(byteBuf));
+ }
+
+ return new DefaultRegisterBuilder()
+ .withIsProxyMapReply(proxyMapReplyFlag)
+ .withIsWantMapNotify(wantMapNotifyFlag)
+ .withRecordCount(recordCount)
+ .withNonce(nonce)
+ .withKeyId(keyId)
+ .withAuthenticationData(authData)
+ .withMapRecords(mapRecords)
+ .build();
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReply.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReply.java
index ee42dd1..6e5e218 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReply.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReply.java
@@ -17,6 +17,7 @@
import com.google.common.base.Objects;
import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
import org.onosproject.lisp.msg.exceptions.LispParseError;
import static com.google.common.base.MoreObjects.toStringHelper;
@@ -176,9 +177,47 @@
*/
private static class ReplyReader implements LispMessageReader<LispMapReply> {
+ private static final int PROBE_INDEX = 3;
+ private static final int ETR_INDEX = 2;
+ private static final int SECURITY_INDEX = 1;
+ private static final int RESERVED_SKIP_LENGTH = 2;
+
@Override
public LispMapReply readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ if (byteBuf.readerIndex() != 0) {
+ return null;
+ }
+
+ byte typeWithFlags = byteBuf.readByte();
+
+ // probe -> 1 bit
+ boolean probe = ByteOperator.getBit(typeWithFlags, PROBE_INDEX);
+
+ // etr -> 1bit
+ boolean etr = ByteOperator.getBit(typeWithFlags, ETR_INDEX);
+
+ // security -> 1 bit
+ boolean security = ByteOperator.getBit(typeWithFlags, SECURITY_INDEX);
+
+ // skip two bytes as they represent reserved fields
+ byteBuf.skipBytes(RESERVED_SKIP_LENGTH);
+
+ // record count -> 8 bits
+ byte recordCount = (byte) byteBuf.readUnsignedByte();
+
+ // nonce -> 64 bits
+ long nonce = byteBuf.readLong();
+
+ // TODO: need to de-serialize EID-RLOC records
+
+ return new DefaultReplyBuilder()
+ .withIsProbe(probe)
+ .withIsEtr(etr)
+ .withIsSecurity(security)
+ .withRecordCount(recordCount)
+ .withNonce(nonce)
+ .build();
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequest.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequest.java
index f988fa1..d2d7bdb 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequest.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequest.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import io.netty.buffer.ByteBuf;
+import org.onlab.util.ByteOperator;
import org.onosproject.lisp.msg.exceptions.LispParseError;
import org.onosproject.lisp.msg.types.LispAfiAddress;
@@ -227,7 +228,6 @@
return this;
}
-
@Override
public RequestBuilder withIsSmr(boolean smr) {
this.smr = smr;
@@ -265,14 +265,14 @@
}
@Override
- public RequestBuilder addItrRloc(LispAfiAddress itrRloc) {
- this.itrRlocs.add(itrRloc);
+ public RequestBuilder withItrRlocs(List<LispAfiAddress> itrRlocs) {
+ this.itrRlocs = ImmutableList.copyOf(itrRlocs);
return this;
}
@Override
- public RequestBuilder addEidRecord(LispEidRecord record) {
- this.eidRecords.add(record);
+ public RequestBuilder withEidRecords(List<LispEidRecord> records) {
+ this.eidRecords = ImmutableList.copyOf(records);
return this;
}
@@ -288,9 +288,69 @@
*/
private static class RequestReader implements LispMessageReader<LispMapRequest> {
+ private static final int AUTHORITATIVE_INDEX = 3;
+ private static final int MAP_DATA_PRESENT_INDEX = 2;
+ private static final int PROBE_INDEX = 1;
+ private static final int SMR_INDEX = 0;
+ private static final int PITR_INDEX = 7;
+ private static final int SMR_INVOKED_INDEX = 6;
+
@Override
public LispMapRequest readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ if (byteBuf.readerIndex() != 0) {
+ return null;
+ }
+
+ byte typeWithFlags = byteBuf.readByte();
+
+ // authoritative -> 1 bit
+ boolean authoritative = ByteOperator.getBit(typeWithFlags, AUTHORITATIVE_INDEX);
+
+ // mapDataPresent -> 1 bit
+ boolean mapDataPresent = ByteOperator.getBit(typeWithFlags, MAP_DATA_PRESENT_INDEX);
+
+ // probe -> 1 bit
+ boolean probe = ByteOperator.getBit(typeWithFlags, PROBE_INDEX);
+
+ // smr -> 1 bit
+ boolean smr = ByteOperator.getBit(typeWithFlags, SMR_INDEX);
+
+ byte reservedWithFlags = byteBuf.readByte();
+
+ // pitr -> 1 bit
+ boolean pitr = ByteOperator.getBit(reservedWithFlags, PITR_INDEX);
+
+ // smrInvoked -> 1 bit
+ boolean smrInvoked = ByteOperator.getBit(reservedWithFlags, SMR_INVOKED_INDEX);
+
+ // let's skip reserved field, only obtains ITR counter value
+ // assume that first 3 bits are all set as 0,
+ // remain 5 bits represent Itr Rloc Counter (IRC)
+ int irc = byteBuf.readUnsignedShort();
+
+ // record count -> 8 bits
+ int recordCount = byteBuf.readUnsignedShort();
+
+ // nonce -> 64 bits
+ long nonce = byteBuf.readLong();
+
+ // TODO: de-serialize source EID AFI and address
+
+ // TODO: de-serialize ITR-RLOC AFI and address
+
+ // TODO: de-serialize EID-RECORD
+
+ return new DefaultRequestBuilder()
+ .withIsAuthoritative(authoritative)
+ .withIsMapDataPresent(mapDataPresent)
+ .withIsProbe(probe)
+ .withIsSmr(smr)
+ .withIsPitr(pitr)
+ .withIsSmrInvoked(smrInvoked)
+ .withNonce(nonce)
+ .withRecordCount((byte) recordCount)
+ .build();
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispEidRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispEidRecord.java
index 0984324..498ce13 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispEidRecord.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispEidRecord.java
@@ -61,9 +61,20 @@
*/
private static class EidRecordReader implements LispMessageReader<LispEidRecord> {
+ private static final int RESERVED_SKIP_LENGTH = 1;
+
@Override
public LispEidRecord readFrom(ByteBuf byteBuf) throws LispParseError {
- return null;
+
+ // let's skip the reserved field
+ byteBuf.skipBytes(RESERVED_SKIP_LENGTH);
+
+ short maskLength = (short) byteBuf.readUnsignedShort();
+
+ // TODO: need to de-serialize AFI address
+ LispAfiAddress prefix = null;
+
+ return new LispEidRecord((byte) maskLength, prefix);
}
}
}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispLocatorRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispLocatorRecord.java
new file mode 100644
index 0000000..ca18e0b
--- /dev/null
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispLocatorRecord.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2016-present 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.onosproject.lisp.msg.protocols;
+
+import io.netty.buffer.ByteBuf;
+import org.onosproject.lisp.msg.types.LispAfiAddress;
+
+/**
+ * LISP locator record section which is part of LISP map record.
+ */
+public interface LispLocatorRecord {
+
+ /**
+ * Obtains priority value.
+ *
+ * @return priority value
+ */
+ byte getPriority();
+
+ /**
+ * Obtains weight value.
+ *
+ * @return weight value
+ */
+ byte getWeight();
+
+ /**
+ * Obtains multi-cast priority value.
+ *
+ * @return multi-cast priority value
+ */
+ byte getMulticastPriority();
+
+ /**
+ * Obtains multi-cast weight value.
+ *
+ * @return multi-cast weight value
+ */
+ byte getMulticastWeight();
+
+ /**
+ * Obtains local locator flag.
+ *
+ * @return local locator flag
+ */
+ boolean isLocalLocator();
+
+ /**
+ * Obtains RLOC probed flag.
+ *
+ * @return RLOC probed flag
+ */
+ boolean isRlocProbed();
+
+ /**
+ * Obtains routed flag.
+ *
+ * @return routed flag
+ */
+ boolean isRouted();
+
+ /**
+ * Obtains locator AFI.
+ *
+ * @return locator AFI
+ */
+ LispAfiAddress getLocatorAfi();
+
+ /**
+ * Writes LISP message object into communication channel.
+ *
+ * @param byteBuf byte buffer
+ */
+ void writeTo(ByteBuf byteBuf);
+
+ /**
+ * A builder of LISP locator record.
+ */
+ interface LocatorRecordBuilder {
+
+ /**
+ * Sets priority value.
+ *
+ * @param priority priority
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withPriority(byte priority);
+
+ /**
+ * Sets weight value.
+ *
+ * @param weight weight
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withWeight(byte weight);
+
+ /**
+ * Sets multi-cast priority value.
+ *
+ * @param priority priority
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withMulticastPriority(byte priority);
+
+ /**
+ * Sets multi-cast weight value.
+ *
+ * @param weight weight
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withMulticastWeight(byte weight);
+
+ /**
+ * Sets local locator flag.
+ *
+ * @param localLocator local locator flag
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withLocalLocator(boolean localLocator);
+
+ /**
+ * Sets RLOC probed flag.
+ *
+ * @param rlocProbed RLOC probed flag
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withRlocProbed(boolean rlocProbed);
+
+ /**
+ * Sets routed flag.
+ *
+ * @param routed routed flag
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withRouted(boolean routed);
+
+ /**
+ * Sets locator AFI.
+ *
+ * @param locatorAfi locator AFI
+ * @return LocatorRecordBuilder object
+ */
+ LocatorRecordBuilder withLocatorAfi(LispAfiAddress locatorAfi);
+
+ /**
+ * Builds locator record.
+ *
+ * @return locator record instance
+ */
+ LispLocatorRecord build();
+ }
+}
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapNotify.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapNotify.java
index 8e33dba..84830e9 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapNotify.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapNotify.java
@@ -129,12 +129,12 @@
NotifyBuilder withAuthenticationData(byte[] authenticationData);
/**
- * Adds a new record to record list.
+ * Sets a collection of map records.
*
- * @param record record
- * @return NotifyBuilder object
+ * @param mapRecords a collection of map records
+ * @return RegisterBuilder object
*/
- NotifyBuilder addRecord(LispMapRecord record);
+ NotifyBuilder withMapRecords(List<LispMapRecord> mapRecords);
/**
* Builds LISP map notify message.
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRecord.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRecord.java
index 6374292..49ff541 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRecord.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRecord.java
@@ -15,8 +15,11 @@
*/
package org.onosproject.lisp.msg.protocols;
+import io.netty.buffer.ByteBuf;
import org.onosproject.lisp.msg.types.LispAfiAddress;
+import java.util.List;
+
/**
* LISP record section which is part of LISP map register message.
*/
@@ -72,6 +75,20 @@
LispAfiAddress getEidPrefixAfi();
/**
+ * Obtains a collection of locator records.
+ *
+ * @return a collection of locator records
+ */
+ List<LispLocatorRecord> getLocators();
+
+ /**
+ * Writes LISP message object into communication channel.
+ *
+ * @param byteBuf byte buffer
+ */
+ void writeTo(ByteBuf byteBuf);
+
+ /**
* A builder of LISP map record.
*/
interface MapRecordBuilder {
@@ -133,6 +150,14 @@
MapRecordBuilder withEidPrefixAfi(LispAfiAddress prefix);
/**
+ * Sets a collection of locator records.
+ *
+ * @param records a collection of locator records
+ * @return MapRecordBuilder object
+ */
+ MapRecordBuilder withLocators(List<LispLocatorRecord> records);
+
+ /**
* Builds map record.
*
* @return map record instance
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRegister.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRegister.java
index a899b43..d3b1e6f 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRegister.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRegister.java
@@ -159,12 +159,12 @@
RegisterBuilder withAuthenticationData(byte[] authenticationData);
/**
- * Adds a new record to record list.
+ * Sets a collection of map records.
*
- * @param record record
+ * @param mapRecords a collection of map records
* @return RegisterBuilder object
*/
- RegisterBuilder addRecord(LispMapRecord record);
+ RegisterBuilder withMapRecords(List<LispMapRecord> mapRecords);
/**
* Builds LISP map register message.
diff --git a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRequest.java b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRequest.java
index 57f87b2..f548f65 100644
--- a/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRequest.java
+++ b/protocols/lisp/msg/src/main/java/org/onosproject/lisp/msg/protocols/LispMapRequest.java
@@ -210,20 +210,20 @@
RequestBuilder withSourceEid(LispAfiAddress sourceEid);
/**
- * Adds ITR RLOC into RLOC collection.
+ * Sets a collection of ITR RLOCs.
*
- * @param itrRloc ITR RLOC
+ * @param itrRlocs a collection of ITR RLOCs
* @return RequestBuilder object
*/
- RequestBuilder addItrRloc(LispAfiAddress itrRloc);
+ RequestBuilder withItrRlocs(List<LispAfiAddress> itrRlocs);
/**
- * Adds EID record into record collection.
+ * Sets a collection of EID records.
*
- * @param record EID record
+ * @param records a collection of EID records
* @return RequestBuilder object
*/
- RequestBuilder addEidRecord(LispEidRecord record);
+ RequestBuilder withEidRecords(List<LispEidRecord> records);
/**
* Builds LISP map request message.
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecordTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecordTest.java
new file mode 100644
index 0000000..7b99612
--- /dev/null
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispLocatorRecordTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2016-present 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.onosproject.lisp.msg.protocols;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Unit tests for DefaultLispLocatorRecord class.
+ */
+public final class DefaultLispLocatorRecordTest {
+
+ private LispLocatorRecord record1;
+ private LispLocatorRecord sameAsRecord1;
+ private LispLocatorRecord record2;
+
+ @Before
+ public void setup() {
+
+ LispLocatorRecord.LocatorRecordBuilder builder1 =
+ new DefaultLispLocatorRecord.DefaultLocatorRecordBuilder();
+
+ record1 = builder1
+ .withPriority((byte) 0x01)
+ .withWeight((byte) 0x01)
+ .withMulticastPriority((byte) 0x01)
+ .withMulticastWeight((byte) 0x01)
+ .withLocalLocator(true)
+ .withRlocProbed(false)
+ .withRouted(true)
+ .build();
+
+ LispLocatorRecord.LocatorRecordBuilder builder2 =
+ new DefaultLispLocatorRecord.DefaultLocatorRecordBuilder();
+
+ sameAsRecord1 = builder2
+ .withPriority((byte) 0x01)
+ .withWeight((byte) 0x01)
+ .withMulticastPriority((byte) 0x01)
+ .withMulticastWeight((byte) 0x01)
+ .withLocalLocator(true)
+ .withRlocProbed(false)
+ .withRouted(true)
+ .build();
+
+ LispLocatorRecord.LocatorRecordBuilder builder3 =
+ new DefaultLispLocatorRecord.DefaultLocatorRecordBuilder();
+
+ record2 = builder3
+ .withPriority((byte) 0x02)
+ .withWeight((byte) 0x02)
+ .withMulticastPriority((byte) 0x02)
+ .withMulticastWeight((byte) 0x02)
+ .withLocalLocator(false)
+ .withRlocProbed(true)
+ .withRouted(false)
+ .build();
+ }
+
+ @Test
+ public void testEquality() {
+ new EqualsTester()
+ .addEqualityGroup(record1, sameAsRecord1)
+ .addEqualityGroup(record2).testEquals();
+ }
+
+ @Test
+ public void testConstruction() {
+ DefaultLispLocatorRecord record = (DefaultLispLocatorRecord) record1;
+
+ assertThat(record.getPriority(), is((byte) 0x01));
+ assertThat(record.getWeight(), is((byte) 0x01));
+ assertThat(record.getMulticastPriority(), is((byte) 0x01));
+ assertThat(record.getMulticastWeight(), is((byte) 0x01));
+ assertThat(record.isLocalLocator(), is(true));
+ assertThat(record.isRlocProbed(), is(false));
+ assertThat(record.isRouted(), is(true));
+ }
+}
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotifyTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotifyTest.java
index 219d6ab..26d8ff0 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotifyTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapNotifyTest.java
@@ -25,7 +25,7 @@
/**
* Unit tests for DefaultLispMapNotify class.
*/
-public class DefaultLispMapNotifyTest {
+public final class DefaultLispMapNotifyTest {
private LispMapNotify notify1;
private LispMapNotify sameAsNotify1;
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecordTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecordTest.java
index 78af45d..1c85b11 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecordTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRecordTest.java
@@ -25,7 +25,7 @@
/**
* Unit tests for DefaultLispMapRecord class.
*/
-public class DefaultLispMapRecordTest {
+public final class DefaultLispMapRecordTest {
private LispMapRecord record1;
private LispMapRecord sameAsRecord1;
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegisterTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegisterTest.java
index 940345c..1b0a5a4 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegisterTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRegisterTest.java
@@ -25,7 +25,7 @@
/**
* Unit tests for DefaultLispMapRegister class.
*/
-public class DefaultLispMapRegisterTest {
+public final class DefaultLispMapRegisterTest {
private LispMapRegister register1;
private LispMapRegister sameAsRegister1;
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
index e078cd9..7b530d2 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapReplyTest.java
@@ -25,7 +25,7 @@
/**
* Unit tests for DefaultLispMapReply class.
*/
-public class DefaultLispMapReplyTest {
+public final class DefaultLispMapReplyTest {
private LispMapReply reply1;
private LispMapReply sameAsReply1;
diff --git a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequestTest.java b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequestTest.java
index 13fafc8..155c6a6 100644
--- a/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequestTest.java
+++ b/protocols/lisp/msg/src/test/java/org/onosproject/lisp/msg/protocols/DefaultLispMapRequestTest.java
@@ -25,7 +25,7 @@
/**
* Unit tests for DefaultLispMapRequest class.
*/
-public class DefaultLispMapRequestTest {
+public final class DefaultLispMapRequestTest {
private LispMapRequest request1;
private LispMapRequest sameAsRequest1;
diff --git a/utils/misc/src/main/java/org/onlab/util/ByteOperator.java b/utils/misc/src/main/java/org/onlab/util/ByteOperator.java
new file mode 100644
index 0000000..4d30ce7
--- /dev/null
+++ b/utils/misc/src/main/java/org/onlab/util/ByteOperator.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2016-present 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.util;
+
+/**
+ * Provide a set of byte operations.
+ */
+public final class ByteOperator {
+
+ /**
+ * Private constructor which prevents from external instantiation.
+ */
+ private ByteOperator() {
+
+ }
+
+ /**
+ * Obtains a specific bit value from a byte with given index number.
+ *
+ * @param value byte value
+ * @param index index number
+ * @return a specific bit value from a byte
+ */
+ public static boolean getBit(byte value, int index) {
+ // the length of byte should always be positive whiles less than 8
+ if (index > 7 || index < 0) {
+ return false;
+ }
+
+ int bitMask = getHex((int) Math.pow(2, index));
+ return (value & bitMask) == bitMask;
+ }
+
+ /**
+ * Converts boolean value into bit.
+ *
+ * @param value boolean value
+ * @param bit bit value
+ * @return converted bit value
+ */
+ public static byte toBit(boolean value, int bit) {
+ return (byte) (value ? bit : 0x00);
+ }
+
+ /**
+ * Convert decimal integer into hex integer.
+ *
+ * @param decimal decimal formatted integer
+ * @return hex formatted integer
+ */
+ private static int getHex(int decimal) {
+ return Integer.valueOf(String.valueOf(decimal), 16);
+ }
+}
diff --git a/utils/misc/src/test/java/org/onlab/util/ByteOperatorTest.java b/utils/misc/src/test/java/org/onlab/util/ByteOperatorTest.java
new file mode 100644
index 0000000..21dffbe
--- /dev/null
+++ b/utils/misc/src/test/java/org/onlab/util/ByteOperatorTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016-present 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.util;
+
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Unit tests for ByteOperator.
+ */
+public class ByteOperatorTest {
+
+ @Test
+ public void testGetBit() {
+ byte eight = 0x08;
+ assertThat(ByteOperator.getBit(eight, 0), is(false));
+ assertThat(ByteOperator.getBit(eight, 1), is(false));
+ assertThat(ByteOperator.getBit(eight, 2), is(false));
+ assertThat(ByteOperator.getBit(eight, 3), is(true));
+ assertThat(ByteOperator.getBit(eight, 4), is(false));
+ assertThat(ByteOperator.getBit(eight, 5), is(false));
+ assertThat(ByteOperator.getBit(eight, 6), is(false));
+ assertThat(ByteOperator.getBit(eight, 7), is(false));
+
+ byte one = 0x01;
+ assertThat(ByteOperator.getBit(one, 0), is(true));
+ assertThat(ByteOperator.getBit(one, 1), is(false));
+ assertThat(ByteOperator.getBit(one, 2), is(false));
+ assertThat(ByteOperator.getBit(one, 3), is(false));
+ assertThat(ByteOperator.getBit(one, 4), is(false));
+ assertThat(ByteOperator.getBit(one, 5), is(false));
+ assertThat(ByteOperator.getBit(one, 6), is(false));
+ assertThat(ByteOperator.getBit(one, 7), is(false));
+ }
+}