package org.opendaylight.protocol.bgp.parser.impl.message;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
import static java.util.Objects.requireNonNull;
+import com.google.common.collect.ImmutableList;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPError;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
import org.opendaylight.protocol.bgp.parser.spi.MessageParser;
import org.opendaylight.protocol.bgp.parser.spi.MessageSerializer;
import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterLengthOverflowException;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
import org.opendaylight.protocol.util.Ipv4Util;
import org.opendaylight.protocol.util.Values;
private static final int BGP_ID_SIZE = 4;
private static final int OPT_PARAM_LENGTH_SIZE = 1;
+ // Optional Parameter Type for Extended Optional Parameters Length
+ private static final int OPT_PARAM_EXT_PARAM = 255;
+
private static final int MIN_MSG_LENGTH = VERSION_SIZE + AS_SIZE
+ HOLD_TIME_SIZE + BGP_ID_SIZE + OPT_PARAM_LENGTH_SIZE;
msgBody.writeShort(open.getHoldTimer());
msgBody.writeBytes(Ipv4Util.bytesForAddress(open.getBgpIdentifier()));
- final ByteBuf paramsBuffer = Unpooled.buffer();
- final List<BgpParameters> params = open.getBgpParameters();
- if (params != null) {
- for (final BgpParameters param : params) {
- this.reg.serializeParameter(param, paramsBuffer);
+ serializeParameters(open.getBgpParameters(), msgBody);
+
+ MessageUtil.formatMessage(TYPE, msgBody, bytes);
+ }
+
+ private void serializeParameters(final List<BgpParameters> params, final ByteBuf msgBody) {
+ if (params == null || params.isEmpty()) {
+ msgBody.writeByte(0);
+ return;
+ }
+
+ final ByteBuf normal = normalSerializeParameters(params);
+ if (normal != null) {
+ final int length = normal.writerIndex();
+ verify(length <= Values.UNSIGNED_BYTE_MAX_VALUE);
+ msgBody.writeByte(length);
+ msgBody.writeBytes(normal);
+ return;
+ }
+
+ final ByteBuf buffer = Unpooled.buffer();
+ for (final BgpParameters param : params) {
+ final Optional<ParameterSerializer> optSer = reg.findSerializer(param);
+ if (optSer.isPresent()) {
+ optSer.get().serializeExtendedParameter(param, buffer);
+ } else {
+ LOG.debug("Ignoring unregistered parameter {}", param);
}
}
- final int paramsLength = paramsBuffer.writerIndex();
- if (paramsLength > 255) {
- LOG.error("OPEN message message optional parameter length {} exceeds maximum length supported by BGP. "
- + "Adjust advertized capabilities toward the peer to fit into limit of 255 bytes by trimming {}",
- paramsLength, params);
- throw new IllegalArgumentException(String.format(
- "Cannot encode OPEN message because optional parameter length %s exceeds length field size.",
- paramsLength));
+
+ final int length = buffer.writerIndex();
+ checkState(length <= Values.UNSIGNED_SHORT_MAX_VALUE);
+
+ // The non-extended Optional Parameters Length field MUST be set to 255
+ msgBody.writeByte(Values.UNSIGNED_BYTE_MAX_VALUE);
+ // The subsequent one-octet field, that in the non-extended format would
+ // be the first Optional Parameter Type field, MUST be set to 255
+ msgBody.writeByte(OPT_PARAM_EXT_PARAM);
+ // Extended Optional Parameters Length
+ msgBody.writeShort(length);
+ msgBody.writeBytes(buffer);
+ }
+
+ private ByteBuf normalSerializeParameters(final List<BgpParameters> params) {
+ final ByteBuf buffer = Unpooled.buffer();
+ for (final BgpParameters param : params) {
+ final Optional<ParameterSerializer> optSer = reg.findSerializer(param);
+ if (optSer.isPresent()) {
+ try {
+ optSer.get().serializeParameter(param, buffer);
+ } catch (ParameterLengthOverflowException e) {
+ LOG.debug("Forcing extended parameter serialization", e);
+ return null;
+ }
+ } else {
+ LOG.debug("Ingnoring unregistered parameter {}", param);
+ }
}
- msgBody.writeByte(paramsLength);
- msgBody.writeBytes(paramsBuffer);
- MessageUtil.formatMessage(TYPE, msgBody, bytes);
+ final int length = buffer.writerIndex();
+ if (length > Values.UNSIGNED_BYTE_MAX_VALUE) {
+ LOG.debug("Final parameter size is {}, forcing extended serialization", length);
+ return null;
+ }
+
+ return buffer;
}
/**
throw new BGPDocumentedException("BGP Identifier is not a valid IPv4 Address", BGPError.BAD_BGP_ID, e);
}
final int optLength = body.readUnsignedByte();
-
- final List<BgpParameters> optParams = new ArrayList<>();
- if (optLength > 0) {
- fillParams(body.slice(), optParams);
- }
+ final List<BgpParameters> optParams = parseParameters(body.slice(), optLength);
LOG.debug("BGP Open message was parsed: AS = {}, holdTimer = {}, bgpId = {}, optParams = {}", as,
holdTime, bgpId, optParams);
return new OpenBuilder().setMyAsNumber(as.getValue().intValue()).setHoldTimer(holdTime)
.setBgpIdentifier(bgpId).setBgpParameters(optParams).build();
}
- private void fillParams(final ByteBuf buffer, final List<BgpParameters> params) throws BGPDocumentedException {
- checkArgument(buffer != null && buffer.isReadable(), "Buffer cannot be null or empty.");
+ private List<BgpParameters> parseParameters(final ByteBuf buffer, final int length) throws BGPDocumentedException {
+ if (length == 0) {
+ return ImmutableList.of();
+ }
if (LOG.isTraceEnabled()) {
- LOG.trace("Started parsing of BGP parameter: {}", ByteBufUtil.hexDump(buffer));
+ LOG.trace("Started parsing of BGP parameter: {} length {}", ByteBufUtil.hexDump(buffer), length);
+ }
+
+ final int realLength;
+ final OptionalInt extendedLength = extractExtendedLength(buffer, length);
+ if (extendedLength.isPresent()) {
+ realLength = extendedLength.getAsInt();
+ if (realLength < Values.UNSIGNED_BYTE_MAX_VALUE) {
+ LOG.debug("Peer used Extended Optional Parameters Length to encode length {}", realLength);
+ }
+ } else {
+ realLength = length;
+ }
+
+ // We have determined the real length, we can trim the buffer now
+ if (buffer.readableBytes() > realLength) {
+ buffer.writerIndex(buffer.readerIndex() + realLength);
+ LOG.trace("Truncated BGP parameter buffer to length {}: {}", realLength, ByteBufUtil.hexDump(buffer));
}
+
+ final int lengthSize = extendedLength.isPresent() ? 1 : 2;
+ final List<BgpParameters> params = new ArrayList<>();
while (buffer.isReadable()) {
- if (buffer.readableBytes() <= 2) {
+ final int paramType = buffer.readUnsignedByte();
+ final Optional<ParameterParser> parser = reg.findParser(paramType);
+ if (!parser.isPresent()) {
+ throw new BGPDocumentedException("Parameter " + paramType + " not supported",
+ BGPError.OPT_PARAM_NOT_SUPPORTED);
+ }
+ if (buffer.readableBytes() <= lengthSize) {
throw new BGPDocumentedException("Malformed parameter encountered (" + buffer.readableBytes()
- + " bytes left)", BGPError.OPT_PARAM_NOT_SUPPORTED);
+ + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
}
- final int paramType = buffer.readUnsignedByte();
- final int paramLength = buffer.readUnsignedByte();
+ final int paramLength = extendedLength.isPresent() ? buffer.readUnsignedShort() : buffer.readUnsignedByte();
final ByteBuf paramBody = buffer.readSlice(paramLength);
final BgpParameters param;
try {
- param = this.reg.parseParameter(paramType, paramBody);
+ param = parser.get().parseParameter(paramBody);
} catch (final BGPParsingException e) {
throw new BGPDocumentedException("Optional parameter not parsed", BGPError.UNSPECIFIC_OPEN_ERROR, e);
}
- if (param != null) {
- params.add(param);
- } else {
- LOG.debug("Ignoring BGP Parameter type: {}", paramType);
- }
+
+ params.add(verifyNotNull(param));
}
+
LOG.trace("Parsed BGP parameters: {}", params);
+ return params;
+ }
+
+ private static OptionalInt extractExtendedLength(final ByteBuf buffer, final int length)
+ throws BGPDocumentedException {
+ final int type = buffer.markReaderIndex().readUnsignedByte();
+ if (type != OPT_PARAM_EXT_PARAM) {
+ // Not extended length
+ buffer.resetReaderIndex();
+ return OptionalInt.empty();
+ }
+ if (length != Values.UNSIGNED_BYTE_MAX_VALUE) {
+ LOG.debug("Peer uses Extended Optional Parameters Length, but indicated RFC4271 length as {}", length);
+ }
+ if (length < 3) {
+ throw new BGPDocumentedException("Malformed Extended Length parameter encountered ("
+ + (length - 1) + " bytes left)", BGPError.UNSPECIFIC_OPEN_ERROR);
+ }
+ final int avail = buffer.readableBytes();
+ if (avail < 2) {
+ throw new BGPDocumentedException("Buffer underrun: require 2 bytes, only " + avail + " bytes left",
+ BGPError.UNSPECIFIC_OPEN_ERROR);
+ }
+
+ return OptionalInt.of(buffer.readUnsignedShort());
}
}
*/
package org.opendaylight.protocol.bgp.parser.impl.message.open;
+import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
-import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
import org.opendaylight.protocol.bgp.parser.spi.CapabilityRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterLengthOverflowException;
import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
import org.opendaylight.protocol.bgp.parser.spi.ParameterUtil;
@Override
public BgpParameters parseParameter(final ByteBuf buffer) throws BGPParsingException, BGPDocumentedException {
- Preconditions.checkArgument(buffer != null && buffer.readableBytes() != 0,
- "Byte array cannot be null or empty.");
+ checkArgument(buffer != null && buffer.readableBytes() != 0, "Byte array cannot be null or empty.");
if (LOG.isTraceEnabled()) {
LOG.trace("Started parsing of BGP Capabilities: {}", Arrays.toString(ByteArray.getAllBytes(buffer)));
}
@Override
- public void serializeParameter(final BgpParameters parameter, final ByteBuf byteAggregator) {
- if (parameter.getOptionalCapabilities() != null) {
- final ByteBuf buffer = Unpooled.buffer();
- for (final OptionalCapabilities optionalCapa : parameter.getOptionalCapabilities()) {
- LOG.trace("Started serializing BGP Capability: {}", optionalCapa);
- serializeOptionalCapability(optionalCapa, buffer);
- }
- ParameterUtil.formatParameter(TYPE, buffer, byteAggregator);
+ public void serializeParameter(final BgpParameters parameter, final ByteBuf output)
+ throws ParameterLengthOverflowException {
+ final ByteBuf buffer = serializeOptionalCapabilities(parameter.getOptionalCapabilities());
+ if (buffer != null) {
+ ParameterUtil.formatParameter(TYPE, buffer, output);
+ }
+ }
+
+ @Override
+ public void serializeExtendedParameter(final BgpParameters parameter, final ByteBuf output) {
+ final ByteBuf buffer = serializeOptionalCapabilities(parameter.getOptionalCapabilities());
+ if (buffer != null) {
+ ParameterUtil.formatExtendedParameter(TYPE, buffer, output);
+ }
+ }
+
+ private ByteBuf serializeOptionalCapabilities(final List<OptionalCapabilities> capabilities) {
+ if (capabilities == null) {
+ return null;
+ }
+
+ final ByteBuf buffer = Unpooled.buffer();
+ for (final OptionalCapabilities optionalCapa : capabilities) {
+ LOG.trace("Started serializing BGP Capability: {}", optionalCapa);
+ serializeOptionalCapability(optionalCapa, buffer);
}
+ return buffer;
}
private void serializeOptionalCapability(final OptionalCapabilities optionalCapa, final ByteBuf byteAggregator) {
final CParameters cap = optionalCapa.getCParameters();
final ByteBuf bytes = Unpooled.buffer();
this.reg.serializeCapability(cap, bytes);
- Preconditions.checkArgument(bytes != null, "Unhandled capability class %s", cap.implementedInterface());
+ checkArgument(bytes != null, "Unhandled capability class %s", cap.implementedInterface());
if (LOG.isTraceEnabled()) {
LOG.trace("BGP capability serialized to: {}", ByteBufUtil.hexDump(bytes));
import static org.junit.Assert.assertEquals;
-import com.google.common.collect.Lists;
+import com.google.common.collect.ImmutableList;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.impl.message.BGPOpenMessageParser;
import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
import org.opendaylight.protocol.util.ByteArray;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Open;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.OpenBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.ProtocolVersion;
-import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParametersBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.OptionalCapabilities;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.OptionalCapabilitiesBuilder;
public class OpenTest {
@Test
- public void testSerializeOpen() throws Exception {
- final List<OptionalCapabilities> optionalCapas = Lists.newArrayList();
- final OptionalCapabilities optionalCapabilitiesBuilder = new OptionalCapabilitiesBuilder()
- .setCParameters(new CParametersBuilder()
- .setAs4BytesCapability(new As4BytesCapabilityBuilder().setAsNumber(new AsNumber(1000L)).build())
- .addAugmentation(CParameters1.class, new CParameters1Builder()
- .setGracefulRestartCapability(new GracefulRestartCapabilityBuilder()
- .setRestartFlags(new GracefulRestartCapability.RestartFlags(false)).setRestartTime(0)
- .setTables(Collections.emptyList()).build())
- .build())
- .build())
- .build();
- optionalCapas.add(optionalCapabilitiesBuilder);
- final List<BgpParameters> tlvs = Lists.newArrayList(new BgpParametersBuilder()
- .setOptionalCapabilities(optionalCapas).build());
+ public void testSerializeOpen() throws BGPDocumentedException {
final Open open = new OpenBuilder().setBgpIdentifier(new Ipv4Address("127.0.0.1")).setMyAsNumber(30)
- .setHoldTimer(3).setVersion(new ProtocolVersion((short) 4)).setBgpParameters(tlvs).build();
+ .setHoldTimer(3).setVersion(new ProtocolVersion((short) 4)).setBgpParameters(ImmutableList.of(
+ new BgpParametersBuilder()
+ .setOptionalCapabilities(ImmutableList.of(new OptionalCapabilitiesBuilder()
+ .setCParameters(new CParametersBuilder()
+ .setAs4BytesCapability(new As4BytesCapabilityBuilder()
+ .setAsNumber(new AsNumber(1000L)).build())
+ .addAugmentation(CParameters1.class, new CParameters1Builder()
+ .setGracefulRestartCapability(new GracefulRestartCapabilityBuilder()
+ .setRestartFlags(new GracefulRestartCapability.RestartFlags(false))
+ .setRestartTime(0)
+ .setTables(ImmutableList.of()).build())
+ .build())
+ .build())
+ .build())).build()))
+ .build();
final ByteBuf msg = Unpooled.buffer();
new BGPOpenMessageParser(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance().getParameterRegistry())
.serializeMessage(open, msg);
.augmentation(CParameters1.class).getGracefulRestartCapability());
}
+
+ @Test
+ public void testSerializeLongOpen() throws BGPDocumentedException {
+ final List<OptionalCapabilities> capabilities = new ArrayList<>();
+ for (int i = 0; i < 200; ++i) {
+ capabilities.add(new OptionalCapabilitiesBuilder()
+ .setCParameters(new CParametersBuilder()
+ .setAs4BytesCapability(new As4BytesCapabilityBuilder()
+ .setAsNumber(new AsNumber(1000L)).build())
+ .build())
+ .build());
+ }
+
+ final Open open = new OpenBuilder().setBgpIdentifier(new Ipv4Address("127.0.0.1")).setMyAsNumber(30)
+ .setHoldTimer(3).setVersion(new ProtocolVersion((short) 4)).setBgpParameters(ImmutableList.of(
+ new BgpParametersBuilder().setOptionalCapabilities(capabilities).build()))
+ .build();
+
+ final ByteBuf msg = Unpooled.buffer();
+ new BGPOpenMessageParser(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance().getParameterRegistry())
+ .serializeMessage(open, msg);
+
+ assertEquals(1235, msg.readableBytes());
+
+ final byte[] temp = ByteArray.cutBytes(ByteArray.getAllBytes(msg), 19);
+ final Open openResult = new BGPOpenMessageParser(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance()
+ .getParameterRegistry()).parseMessageBody(Unpooled.copiedBuffer(temp), temp.length, null);
+
+ assertEquals(200, openResult.getBgpParameters().get(0).getOptionalCapabilities().size());
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.protocol.bgp.parser.spi;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+
+/**
+ * Exception reported when a {@link ParameterSerializer} detects its output does not fit 255 bytes and hence cannot
+ * be held in plain RFC4271 OPEN message.
+ */
+@Beta
+public final class ParameterLengthOverflowException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ public ParameterLengthOverflowException(final String message) {
+ super(requireNonNull(message));
+ }
+
+ public static void throwIf(final boolean expression, final String format, final Object... args)
+ throws ParameterLengthOverflowException {
+ if (expression) {
+ throw new ParameterLengthOverflowException(String.format(format, args));
+ }
+ }
+}
*/
package org.opendaylight.protocol.bgp.parser.spi;
-import io.netty.buffer.ByteBuf;
-import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
-import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import java.util.Optional;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
public interface ParameterRegistry {
- BgpParameters parseParameter(int parameterType, ByteBuf bytes) throws BGPParsingException, BGPDocumentedException;
+ /**
+ * Find a parser for specified parameter type.
+ *
+ * @param parameterType Parameter type
+ * @return Parser, if registered
+ */
+ Optional<ParameterParser> findParser(int parameterType);
- void serializeParameter(BgpParameters parameter, ByteBuf bytes);
+ /**
+ * Find a serializer for specified parameter.
+ *
+ * @param parameter Parameter to serialize
+ * @return Serializer, if registered
+ * @throws NullPointerException if any argument is null
+ */
+ Optional<ParameterSerializer> findSerializer(BgpParameters parameter);
}
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
public interface ParameterSerializer {
- void serializeParameter(BgpParameters parameter, ByteBuf byteAggregator);
+ /**
+ * Serialize parameter using RFC4271 encoding.
+ *
+ * @param parameter Parameter to serialize
+ * @param output Output buffer
+ * @throws NullPointerException if any argument is null
+ * @throws ParameterLengthOverflowException when the parameter does not fit into 255 bytes
+ */
+ void serializeParameter(BgpParameters parameter, ByteBuf output) throws ParameterLengthOverflowException;
+
+ /**
+ * Serialize parameter using
+ * <a href="https://tools.ietf.org/html/draft-ietf-idr-ext-opt-param-05">Extended Optional Parameters Length</a>
+ * encoding.
+ *
+ * @param parameter Parameter to serialize
+ * @param output Output buffer
+ * @throws NullPointerException if any argument is null
+ */
+ void serializeExtendedParameter(BgpParameters parameter, ByteBuf output);
}
*/
package org.opendaylight.protocol.bgp.parser.spi;
+import static com.google.common.base.Preconditions.checkArgument;
+
import io.netty.buffer.ByteBuf;
-import io.netty.buffer.ByteBufUtil;
+import org.opendaylight.protocol.util.Values;
/**
* Utility class which is intended for formatting parameter.
}
/**
- * Adds header to parameter value.
+ * Adds header to parameter value in RFC4271 format.
*
* @param type of the parameter
* @param value parameter value
* @param buffer ByteBuf where the parameter will be copied with its header
* @throws IllegalArgumentException if value length exceeds 255 bytes
*/
- public static void formatParameter(final int type, final ByteBuf value, final ByteBuf buffer) {
+ public static void formatParameter(final int type, final ByteBuf value, final ByteBuf buffer)
+ throws ParameterLengthOverflowException {
final int valueLength = value.writerIndex();
- if (valueLength > 255) {
- throw new IllegalArgumentException(String.format(
- "Cannot encode parameter %s because value length %s exceeds parameter length field size (value %s)",
- type, valueLength, ByteBufUtil.hexDump(value)));
- }
+ ParameterLengthOverflowException.throwIf(valueLength > Values.UNSIGNED_BYTE_MAX_VALUE,
+ "Cannot encode %s-byte value", valueLength);
buffer.writeByte(type);
buffer.writeByte(valueLength);
buffer.writeBytes(value);
}
+
+ /**
+ * Adds header to parameter value in draft-ietf-idr-ext-opt-param-05 format.
+ *
+ * @param type of the parameter
+ * @param value parameter value
+ * @param buffer ByteBuf where the parameter will be copied with its header
+ * @throws IllegalArgumentException if value length exceeds 65535 bytes
+ */
+ public static void formatExtendedParameter(final int type, final ByteBuf value, final ByteBuf buffer) {
+ final int valueLength = value.writerIndex();
+ checkArgument(valueLength < Values.UNSIGNED_SHORT_MAX_VALUE, "Cannot encode %s-byte value", valueLength);
+
+ buffer.writeByte(type);
+ buffer.writeShort(valueLength);
+ buffer.writeBytes(value);
+ }
}
import static com.google.common.base.Preconditions.checkArgument;
-import io.netty.buffer.ByteBuf;
-import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
-import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import java.util.Optional;
import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
}
@Override
- public BgpParameters parseParameter(final int parameterType, final ByteBuf buffer) throws BGPParsingException,
- BGPDocumentedException {
- final ParameterParser parser = this.handlers.getParser(parameterType);
- if (parser == null) {
- return null;
- }
- return parser.parseParameter(buffer);
+ public Optional<ParameterParser> findParser(final int parameterType) {
+ return Optional.ofNullable(handlers.getParser(parameterType));
}
@Override
- public void serializeParameter(final BgpParameters parameter, final ByteBuf bytes) {
- final ParameterSerializer serializer = this.handlers.getSerializer(parameter.implementedInterface());
- if (serializer == null) {
- return;
- }
- serializer.serializeParameter(parameter,bytes);
+ public Optional<ParameterSerializer> findSerializer(final BgpParameters parameter) {
+ return Optional.ofNullable(handlers.getSerializer(parameter.implementedInterface()));
}
}
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Mockito.doReturn;
import com.google.common.primitives.UnsignedBytes;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import java.util.Arrays;
import java.util.Optional;
-import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
import org.opendaylight.protocol.util.ByteArray;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.BgpTableType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.Ipv4AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
+@RunWith(MockitoJUnitRunner.StrictStubs.class)
public class UtilsTest {
@Mock private AddressFamilyRegistry afiReg;
@Before
public void setUp() {
- MockitoAnnotations.initMocks(this);
- Mockito.doReturn(1).when(this.afiReg).numberForClass(Ipv4AddressFamily.class);
- Mockito.doReturn(Ipv4AddressFamily.class).when(this.afiReg).classForFamily(1);
- Mockito.doReturn(null).when(this.afiReg).classForFamily(2);
-
- Mockito.doReturn(1).when(this.safiReg).numberForClass(UnicastSubsequentAddressFamily.class);
- Mockito.doReturn(UnicastSubsequentAddressFamily.class).when(this.safiReg).classForFamily(1);
- Mockito.doReturn(null).when(this.safiReg).classForFamily(3);
+ doReturn(1).when(this.afiReg).numberForClass(Ipv4AddressFamily.class);
+ doReturn(Ipv4AddressFamily.class).when(this.afiReg).classForFamily(1);
+ doReturn(null).when(this.afiReg).classForFamily(2);
+
+ doReturn(1).when(this.safiReg).numberForClass(UnicastSubsequentAddressFamily.class);
+ doReturn(UnicastSubsequentAddressFamily.class).when(this.safiReg).classForFamily(1);
+ doReturn(null).when(this.safiReg).classForFamily(3);
}
@Test
}
@Test
- public void testParameterUtil() {
+ public void testParameterUtil() throws ParameterLengthOverflowException {
final byte[] result = new byte[] { 1, 2, 4, 8 };
final ByteBuf aggregator = Unpooled.buffer();
ParameterUtil.formatParameter(1, Unpooled.wrappedBuffer(new byte[] { 4, 8 }), aggregator);
final ByteBuf bytesBuf = Unpooled.copiedBuffer(bytes);
final Optional<BgpTableType> parsedAfiSafi = MultiprotocolCapabilitiesUtil.parseMPAfiSafi(bytesBuf, this.afiReg,
this.safiReg);
- Assert.assertFalse(parsedAfiSafi.isPresent());
+ assertFalse(parsedAfiSafi.isPresent());
}
@Test
final ByteBuf bytesBuf = Unpooled.copiedBuffer(bytes);
final Optional<BgpTableType> parsedAfiSafi = MultiprotocolCapabilitiesUtil.parseMPAfiSafi(bytesBuf, this.afiReg,
this.safiReg);
- Assert.assertFalse(parsedAfiSafi.isPresent());
+ assertFalse(parsedAfiSafi.isPresent());
+ }
+
+ @Test(expected = ParameterLengthOverflowException.class)
+ public void testFormatParameterOverflow() throws ParameterLengthOverflowException {
+ ParameterUtil.formatParameter(2, Unpooled.buffer().writeZero(256), Unpooled.buffer());
+ }
+
+ @Test
+ public void testFormatParameter() throws ParameterLengthOverflowException {
+ final ByteBuf output = Unpooled.buffer();
+ ParameterUtil.formatParameter(2, Unpooled.buffer().writeZero(255), output);
+
+ assertEquals(257, output.readableBytes());
+ assertEquals(2, output.readUnsignedByte());
+ assertEquals(255, output.readUnsignedByte());
+ }
+
+ @Test
+ public void testFormatExtendedParameter() {
+ final ByteBuf output = Unpooled.buffer();
+ ParameterUtil.formatExtendedParameter(2, Unpooled.buffer().writeZero(256), output);
+
+ assertEquals(259, output.readableBytes());
+ assertEquals(2, output.readUnsignedByte());
+ assertEquals(256, output.readUnsignedShort());
}
}
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.protocol.bgp.parser.spi.pojo;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
-import org.junit.Assert;
import org.mockito.Mock;
-import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
import org.opendaylight.protocol.bgp.parser.spi.NextHopParserSerializer;
import org.opendaylight.protocol.bgp.parser.spi.NlriParser;
import org.opendaylight.protocol.bgp.parser.spi.NlriSerializer;
+import org.opendaylight.protocol.bgp.parser.spi.ParameterLengthOverflowException;
import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
private void initMock() {
MockitoAnnotations.initMocks(this);
try {
- Mockito.doNothing().when(this.attrParser).parseAttribute(any(ByteBuf.class), any(AttributesBuilder.class),
+ doNothing().when(this.attrParser).parseAttribute(any(ByteBuf.class), any(AttributesBuilder.class),
any(RevisedErrorHandling.class), any(PeerSpecificParserConstraint.class));
doReturn(EMPTY).when(this.attrParser).toString();
- Mockito.doNothing().when(this.attrSerializer).serializeAttribute(any(Attributes.class), any(ByteBuf.class));
+ doNothing().when(this.attrSerializer).serializeAttribute(any(Attributes.class), any(ByteBuf.class));
doReturn(EMPTY).when(this.attrSerializer).toString();
doReturn(null).when(this.paramParser).parseParameter(any(ByteBuf.class));
doReturn(EMPTY).when(this.paramParser).toString();
- Mockito.doNothing().when(this.paramSerializer).serializeParameter(any(BgpParameters.class),
- any(ByteBuf.class));
+ doNothing().when(this.paramSerializer).serializeParameter(any(BgpParameters.class), any(ByteBuf.class));
doReturn(EMPTY).when(this.paramSerializer).toString();
doReturn(null).when(this.capaParser).parseCapability(any(ByteBuf.class));
doReturn(EMPTY).when(this.capaParser).toString();
- Mockito.doNothing().when(this.capaSerializer).serializeCapability(any(CParameters.class),
- any(ByteBuf.class));
+ doNothing().when(this.capaSerializer).serializeCapability(any(CParameters.class), any(ByteBuf.class));
doReturn(EMPTY).when(this.capaSerializer).toString();
doReturn(null).when(this.sidTlvParser).parseBgpPrefixSidTlv(any(ByteBuf.class));
doReturn(EMPTY).when(this.sidTlvParser).toString();
- Mockito.doNothing().when(this.sidTlvSerializer).serializeBgpPrefixSidTlv(any(BgpPrefixSidTlv.class),
+ doNothing().when(this.sidTlvSerializer).serializeBgpPrefixSidTlv(any(BgpPrefixSidTlv.class),
any(ByteBuf.class));
doReturn(EMPTY).when(this.sidTlvSerializer).toString();
doReturn(0).when(this.sidTlvSerializer).getType();
- doReturn(mock(Notification.class)).when(this.msgParser).parseMessageBody(any(ByteBuf.class),
- Mockito.anyInt(), any(PeerSpecificParserConstraint.class));
+ doReturn(mock(Notification.class)).when(this.msgParser).parseMessageBody(any(ByteBuf.class), anyInt(),
+ any(PeerSpecificParserConstraint.class));
doReturn(EMPTY).when(this.msgParser).toString();
- Mockito.doNothing().when(this.msgSerializer).serializeMessage(any(Notification.class),
- any(ByteBuf.class));
+ doNothing().when(this.msgSerializer).serializeMessage(any(Notification.class), any(ByteBuf.class));
doReturn(EMPTY).when(this.msgSerializer).toString();
- Mockito.doNothing().when(this.nlriParser).parseNlri(any(ByteBuf.class), any(MpUnreachNlriBuilder.class),
- any());
- Mockito.doNothing().when(this.nlriParser).parseNlri(any(ByteBuf.class), any(MpReachNlriBuilder.class),
- any());
+ doNothing().when(this.nlriParser).parseNlri(any(ByteBuf.class), any(MpUnreachNlriBuilder.class), any());
+ doNothing().when(this.nlriParser).parseNlri(any(ByteBuf.class), any(MpReachNlriBuilder.class), any());
doReturn(EMPTY).when(this.nlriParser).toString();
- } catch (BGPDocumentedException | BGPParsingException | BGPTreatAsWithdrawException e) {
- Assert.fail();
+ } catch (BGPDocumentedException | BGPParsingException | BGPTreatAsWithdrawException
+ | ParameterLengthOverflowException e) {
+ throw new IllegalStateException("Mock setup failed", e);
}
}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
+import java.util.Optional;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
final ParameterRegistry paramReg = this.ctx.getParameterRegistry();
final BgpParameters param = mock(BgpParameters.class);
Mockito.doReturn(BgpParameters.class).when(param).implementedInterface();
- final byte[] paramBytes = {
- 0x00, 0x00
- };
- final ByteBuf buffer = Unpooled.buffer(paramBytes.length);
- paramReg.serializeParameter(param, buffer);
- paramReg.parseParameter(0, Unpooled.wrappedBuffer(paramBytes));
- verify(this.activator.paramParser, times(1)).parseParameter(any(ByteBuf.class));
- verify(this.activator.paramSerializer, times(1)).serializeParameter(any(BgpParameters.class),
- any(ByteBuf.class));
+
+ assertEquals(Optional.of(activator.paramParser), paramReg.findParser(0));
+ assertEquals(Optional.of(activator.paramSerializer), paramReg.findSerializer(param));
}
@Test