/* * 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 io.netty.buffer.Unpooled; import org.onosproject.lisp.msg.types.LispAfiAddress; import org.onosproject.lisp.msg.authentication.LispAuthenticationFactory; import org.onosproject.lisp.msg.authentication.LispAuthenticationKeyEnum; 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.types.lcaf.LispLcafAddress.LcafAddressReader; import org.onosproject.lisp.msg.types.lcaf.LispLcafAddress.LcafAddressWriter; import org.onosproject.lisp.msg.types.lcaf.LispNatLcafAddress; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import static com.google.common.base.MoreObjects.toStringHelper; import static org.onosproject.lisp.msg.authentication.LispAuthenticationKeyEnum.valueOf; /** * Default LISP info reply message class. */ public final class DefaultLispInfoReply extends DefaultLispInfo implements LispInfoReply { private static final Logger log = LoggerFactory.getLogger(DefaultLispInfoReply.class); private final LispNatLcafAddress natLcafAddress; static final InfoReplyWriter WRITER; static { WRITER = new InfoReplyWriter(); } /** * A private constructor that protects object instantiation from external. * * @param infoReply info reply flag * @param nonce nonce * @param keyId key identifier * @param authDataLength authentication data length * @param authData authentication data * @param ttl Time-To-Live value * @param maskLength EID prefix mask length * @param eidPrefix EID prefix * @param natLcafAddress NAT LCAF address */ protected DefaultLispInfoReply(boolean infoReply, long nonce, short keyId, short authDataLength, byte[] authData, int ttl, byte maskLength, LispAfiAddress eidPrefix, LispNatLcafAddress natLcafAddress) { super(infoReply, nonce, keyId, authDataLength, authData, ttl, maskLength, eidPrefix); this.natLcafAddress = natLcafAddress; } @Override public LispNatLcafAddress getNatLcafAddress() { return natLcafAddress; } @Override public void writeTo(ByteBuf byteBuf) throws LispWriterException { WRITER.writeTo(byteBuf, this); } @Override public String toString() { return toStringHelper(this) .add("type", getType()) .add("nonce", nonce) .add("keyId", keyId) .add("authentication data length", authDataLength) .add("authentication data", authData) .add("TTL", ttl) .add("EID mask length", maskLength) .add("EID prefix", eidPrefix) .add("NAT LCAF address", natLcafAddress).toString(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } DefaultLispInfoReply that = (DefaultLispInfoReply) o; return Objects.equal(nonce, that.nonce) && Objects.equal(keyId, that.keyId) && Objects.equal(authDataLength, that.authDataLength) && Arrays.equals(authData, that.authData) && Objects.equal(ttl, that.ttl) && Objects.equal(maskLength, that.maskLength) && Objects.equal(eidPrefix, that.eidPrefix) && Objects.equal(natLcafAddress, that.natLcafAddress); } @Override public int hashCode() { return Objects.hashCode(nonce, keyId, authDataLength, ttl, maskLength, eidPrefix, natLcafAddress) + Arrays.hashCode(authData); } public static final class DefaultInfoReplyBuilder implements InfoReplyBuilder { private boolean infoReply; private long nonce; private short keyId; private short authDataLength; private byte[] authData; private String authKey; private int ttl; private byte maskLength; private LispAfiAddress eidPrefix; private LispNatLcafAddress natLcafAddress; @Override public LispType getType() { return LispType.LISP_INFO; } @Override public InfoReplyBuilder withIsInfoReply(boolean infoReply) { this.infoReply = infoReply; return this; } @Override public InfoReplyBuilder withNonce(long nonce) { this.nonce = nonce; return this; } @Override public InfoReplyBuilder withAuthDataLength(short authDataLength) { this.authDataLength = authDataLength; return this; } @Override public InfoReplyBuilder withKeyId(short keyId) { this.keyId = keyId; return this; } @Override public InfoReplyBuilder withAuthData(byte[] authenticationData) { if (authenticationData != null) { this.authData = authenticationData; } return this; } @Override public InfoReplyBuilder withAuthKey(String key) { this.authKey = key; return this; } @Override public InfoReplyBuilder withTtl(int ttl) { this.ttl = ttl; return this; } @Override public InfoReplyBuilder withMaskLength(byte maskLength) { this.maskLength = maskLength; return this; } @Override public InfoReplyBuilder withEidPrefix(LispAfiAddress eidPrefix) { this.eidPrefix = eidPrefix; return this; } @Override public InfoReplyBuilder withNatLcafAddress(LispNatLcafAddress natLcafAddress) { this.natLcafAddress = natLcafAddress; return this; } @Override public LispInfoReply build() { // if authentication data is not specified, we will calculate it if (authData == null) { LispAuthenticationFactory factory = LispAuthenticationFactory.getInstance(); authDataLength = LispAuthenticationKeyEnum.valueOf(keyId).getHashLength(); byte[] tmpAuthData = new byte[authDataLength]; Arrays.fill(tmpAuthData, (byte) 0); authData = tmpAuthData; ByteBuf byteBuf = Unpooled.buffer(); try { new DefaultLispInfoReply(infoReply, nonce, keyId, authDataLength, authData, ttl, maskLength, eidPrefix, natLcafAddress).writeTo(byteBuf); } catch (LispWriterException e) { log.warn("Failed to serialize info reply", e); } byte[] bytes = new byte[byteBuf.readableBytes()]; byteBuf.readBytes(bytes); if (authKey == null) { log.warn("Must specify authentication key"); } authData = factory.createAuthenticationData(valueOf(keyId), authKey, bytes); } return new DefaultLispInfoReply(infoReply, nonce, keyId, authDataLength, authData, ttl, maskLength, eidPrefix, natLcafAddress); } } /** * A LISP message reader for InfoReply message. */ public static final class InfoReplyReader implements LispMessageReader<LispInfoReply> { @Override public LispInfoReply readFrom(ByteBuf byteBuf) throws LispParseError, LispReaderException { LispInfo lispInfo = deserialize(byteBuf); LispNatLcafAddress natLcafAddress = (LispNatLcafAddress) new LcafAddressReader().readFrom(byteBuf); return new DefaultInfoReplyBuilder() .withIsInfoReply(lispInfo.isInfoReply()) .withNonce(lispInfo.getNonce()) .withKeyId(lispInfo.getKeyId()) .withAuthDataLength(lispInfo.getAuthDataLength()) .withAuthData(lispInfo.getAuthData()) .withTtl(lispInfo.getTtl()) .withMaskLength(lispInfo.getMaskLength()) .withEidPrefix(lispInfo.getPrefix()) .withNatLcafAddress(natLcafAddress).build(); } } public static final class InfoReplyWriter implements LispMessageWriter<LispInfoReply> { @Override public void writeTo(ByteBuf byteBuf, LispInfoReply message) throws LispWriterException { serialize(byteBuf, message); new LcafAddressWriter().writeTo(byteBuf, message.getNatLcafAddress()); } } }