/* * Copyright 2017-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 com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import io.netty.buffer.ByteBuf; import org.onlab.packet.DeserializationException; import org.onlab.util.ByteOperator; import org.onosproject.lisp.msg.exceptions.LispParseError; import org.onosproject.lisp.msg.exceptions.LispReaderException; import org.onosproject.lisp.msg.exceptions.LispWriterException; import org.onosproject.lisp.msg.protocols.DefaultLispReferral.ReferralWriter; import org.onosproject.lisp.msg.types.LispAfiAddress; import org.onosproject.lisp.msg.types.LispAfiAddress.AfiAddressWriter; import java.util.List; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.base.Preconditions.checkNotNull; /** * Default LISP referral record class. */ public final class DefaultLispReferralRecord extends AbstractLispRecord implements LispReferralRecord { private final boolean incomplete; private final List<LispReferral> referrals; private final List<LispSignature> signatures; static final ReferralRecordWriter WRITER; static { WRITER = new ReferralRecordWriter(); } /** * A private constructor that protects object instantiation from external. * * @param recordTtl record time-to-live value * @param maskLength mask length * @param action lisp map reply action * @param authoritative authoritative flag * @param mapVersionNumber map version number * @param eidPrefixAfi EID prefix AFI address * @param incomplete incomplete flag value * @param referrals a collection referrals * @param signatures a collection signatures */ private DefaultLispReferralRecord(int recordTtl, byte maskLength, LispMapReplyAction action, boolean authoritative, short mapVersionNumber, LispAfiAddress eidPrefixAfi, boolean incomplete, List<LispReferral> referrals, List<LispSignature> signatures) { super(recordTtl, maskLength, action, authoritative, mapVersionNumber, eidPrefixAfi); this.incomplete = incomplete; this.referrals = referrals; this.signatures = signatures; } @Override public int getReferralCount() { return referrals.size(); } @Override public int getSignatureCount() { return signatures.size(); } @Override public boolean isIncomplete() { return incomplete; } @Override public List<LispReferral> getReferrals() { return ImmutableList.copyOf(referrals); } @Override public List<LispSignature> getSignatures() { return ImmutableList.copyOf(signatures); } @Override public void writeTo(ByteBuf byteBuf) throws LispWriterException { WRITER.writeTo(byteBuf, this); } @Override public String toString() { return toStringHelper(this) .add("record TTL", recordTtl) .add("maskLength", maskLength) .add("action", action) .add("authoritative", authoritative) .add("mapVersionNumber", mapVersionNumber) .add("EID prefix AFI address", eidPrefixAfi) .add("incomplete", incomplete) .add("referrals", referrals) .add("signatures", signatures) .toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DefaultLispReferralRecord that = (DefaultLispReferralRecord) o; return Objects.equal(recordTtl, that.recordTtl) && Objects.equal(maskLength, that.maskLength) && Objects.equal(action, that.action) && Objects.equal(authoritative, that.authoritative) && Objects.equal(mapVersionNumber, that.mapVersionNumber) && Objects.equal(eidPrefixAfi, that.eidPrefixAfi) && Objects.equal(referrals, that.referrals) && Objects.equal(signatures, that.signatures); } @Override public int hashCode() { return Objects.hashCode(recordTtl, maskLength, action, authoritative, mapVersionNumber, eidPrefixAfi, incomplete, referrals, signatures); } public static final class DefaultReferralRecordBuilder extends AbstractRecordBuilder<ReferralRecordBuilder> implements ReferralRecordBuilder { private boolean incomplete; private List<LispReferral> referrals = Lists.newArrayList(); private List<LispSignature> signatures = Lists.newArrayList(); @Override public ReferralRecordBuilder withReferrals(List<LispReferral> referrals) { if (referrals != null) { this.referrals = ImmutableList.copyOf(referrals); } return this; } @Override public ReferralRecordBuilder withSignatures(List<LispSignature> signatures) { if (signatures != null) { this.signatures = ImmutableList.copyOf(signatures); } return this; } @Override public ReferralRecordBuilder withIsIncomplete(boolean incomplete) { this.incomplete = incomplete; return this; } @Override public LispReferralRecord build() { checkNotNull(eidPrefixAfi, "Must specify an EID prefix"); return new DefaultLispReferralRecord(recordTtl, maskLength, action, authoritative, mapVersionNumber, eidPrefixAfi, incomplete, referrals, signatures); } } /** * A LISP message reader for ReferralRecord portion. */ public static final class ReferralRecordReader implements LispMessageReader<LispReferralRecord> { private static final int INCOMPLETE_INDEX = 3; private static final int AUTHORITATIVE_INDEX = 4; private static final int REPLY_ACTION_SHIFT_BIT = 5; private static final int RESERVED_SKIP_LENGTH = 1; @Override public LispReferralRecord readFrom(ByteBuf byteBuf) throws LispParseError, LispReaderException, DeserializationException { // Record TTL -> 32 bits int recordTtl = byteBuf.readInt(); // referral count -> 8 bits int referralCount = byteBuf.readUnsignedByte(); // EID mask length -> 8 bits byte maskLength = (byte) byteBuf.readUnsignedByte(); byte actionWithFlag = (byte) byteBuf.readUnsignedByte(); // action -> 3 bits int actionByte = actionWithFlag >> REPLY_ACTION_SHIFT_BIT; LispMapReplyAction action = LispMapReplyAction.valueOf(actionByte); if (action == null) { action = LispMapReplyAction.NoAction; } // authoritative flag -> 1 bit boolean authoritative = ByteOperator.getBit((byte) (actionWithFlag >> AUTHORITATIVE_INDEX), 0); // incomplete flag -> 1 bit boolean incomplete = ByteOperator.getBit((byte) (actionWithFlag >> INCOMPLETE_INDEX), 0); // 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(); LispAfiAddress eidPrefixAfi = new LispAfiAddress.AfiAddressReader().readFrom(byteBuf); List<LispReferral> referrals = Lists.newArrayList(); for (int i = 0; i < referralCount; i++) { referrals.add(new DefaultLispReferral.ReferralReader().readFrom(byteBuf)); } return new DefaultReferralRecordBuilder() .withRecordTtl(recordTtl) .withMaskLength(maskLength) .withAction(action) .withIsAuthoritative(authoritative) .withIsIncomplete(incomplete) .withMapVersionNumber(mapVersionNumber) .withReferrals(referrals) .withEidPrefixAfi(eidPrefixAfi) .build(); } } /** * A LISP message writer for ReferralRecord portion. */ public static final class ReferralRecordWriter implements LispMessageWriter<LispReferralRecord> { private static final int REPLY_ACTION_SHIFT_BIT = 5; private static final int INCOMPLETE_SHIFT_BIT = 3; private static final int AUTHORITATIVE_SHIFT_BIT = 4; private static final int ENABLE_BIT = 1; private static final int DISABLE_BIT = 0; private static final int UNUSED_ZERO = 0; @Override public void writeTo(ByteBuf byteBuf, LispReferralRecord message) throws LispWriterException { // record TTL byteBuf.writeInt(message.getRecordTtl()); // referral count byteBuf.writeByte((byte) message.getReferrals().size()); // EID mask length byteBuf.writeByte(message.getMaskLength()); // reply action byte action = (byte) (message.getAction().getAction() << REPLY_ACTION_SHIFT_BIT); // authoritative bit byte authoritative = DISABLE_BIT; if (message.isAuthoritative()) { authoritative = ENABLE_BIT << AUTHORITATIVE_SHIFT_BIT; } // incomplete bit byte incomplete = DISABLE_BIT; if (message.isIncomplete()) { incomplete = ENABLE_BIT << INCOMPLETE_SHIFT_BIT; } byteBuf.writeByte((byte) (action + authoritative + incomplete)); // fill zero into reserved field byteBuf.writeByte((short) UNUSED_ZERO); // map version number byteBuf.writeShort(message.getMapVersionNumber()); // EID prefix AFI with EID prefix AfiAddressWriter afiAddressWriter = new AfiAddressWriter(); afiAddressWriter.writeTo(byteBuf, message.getEidPrefixAfi()); // serialize referrals ReferralWriter referralWriter = new ReferralWriter(); List<LispReferral> referrals = message.getReferrals(); for (int i = 0; i < referrals.size(); i++) { referralWriter.writeTo(byteBuf, referrals.get(i)); } } } }