package org.opendaylight.protocol.bgp.flowspec;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.destination.DestinationType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpReachNlriBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.MpUnreachNlriBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.reach.nlri.AdvertizedRoutesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutes;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev130919.update.attributes.mp.unreach.nlri.WithdrawnRoutesBuilder;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.AddressFamily;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev130919.SubsequentAddressFamily;
import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public abstract class AbstractFlowspecNlriParser implements NlriParser, NlriSerializer {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractFlowspecNlriParser.class);
@VisibleForTesting
static final NodeIdentifier FLOWSPEC_NID = new NodeIdentifier(Flowspec.QNAME);
@VisibleForTesting
- protected static final NodeIdentifier FLOWSPEC_TYPE_NID = new NodeIdentifier(FlowspecType.QNAME);
- @VisibleForTesting
- static final NodeIdentifier DEST_PREFIX_NID = new NodeIdentifier(QName.create(DestinationPrefixCase.QNAME, "destination-prefix").intern());
- @VisibleForTesting
- static final NodeIdentifier SOURCE_PREFIX_NID = new NodeIdentifier(QName.create(SourcePrefixCase.QNAME, "source-prefix").intern());
+ static final NodeIdentifier FLOWSPEC_TYPE_NID = new NodeIdentifier(FlowspecType.QNAME);
+ public static final NodeIdentifier DEST_PREFIX_NID = new NodeIdentifier(QName.create(DestinationPrefixCase.QNAME, "destination-prefix").intern());
+ public static final NodeIdentifier SOURCE_PREFIX_NID = new NodeIdentifier(QName.create(SourcePrefixCase.QNAME, "source-prefix").intern());
@VisibleForTesting
static final NodeIdentifier PORTS_NID = new NodeIdentifier(Ports.QNAME);
@VisibleForTesting
@VisibleForTesting
static final NodeIdentifier FRAGMENT_NID = new NodeIdentifier(Fragments.QNAME);
@VisibleForTesting
- static final NodeIdentifier OP_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","op"));
+ public static final NodeIdentifier OP_NID = new NodeIdentifier(QName.create(Flowspec.QNAME.getNamespace(), Flowspec.QNAME.getRevision(), "op"));
@VisibleForTesting
- static final NodeIdentifier VALUE_NID = new NodeIdentifier(QName.create("urn:opendaylight:params:xml:ns:yang:bgp-flowspec","2015-08-07","value"));
+ public static final NodeIdentifier VALUE_NID = new NodeIdentifier(QName.create(Flowspec.QNAME.getNamespace(), Flowspec.QNAME.getRevision(), "value"));
protected static final int NLRI_LENGTH = 1;
protected static final int NLRI_LENGTH_EXTENDED = 2;
/**
* Add this constant to length value to achieve all ones in the leftmost nibble.
*/
- private static final int LENGTH_MAGIC = 61440;
- private static final int MAX_NLRI_LENGTH = 4095;
- private static final int MAX_NLRI_LENGTH_ONE_BYTE = 240;
+ protected static final int LENGTH_MAGIC = 61440;
+ protected static final int MAX_NLRI_LENGTH = 4095;
+ protected static final int MAX_NLRI_LENGTH_ONE_BYTE = 240;
@VisibleForTesting
static final String DO_NOT_VALUE = "do-not";
private static final String FLOW_SEPARATOR = " AND ";
- protected abstract void serializeMpReachNlri(final Attributes1 pathAttributes, final ByteBuf byteAggregator);
+ protected AbstractFlowspecNlriParser(SimpleFlowspecTypeRegistry flowspecTypeRegistry) {
+ this.flowspecTypeRegistry = Preconditions.checkNotNull(flowspecTypeRegistry);
+ }
+
+ protected abstract void serializeMpReachNlri(final DestinationType dstType, final ByteBuf byteAggregator);
- protected abstract void serializeMpUnreachNlri(final Attributes2 pathAttributes, final ByteBuf byteAggregator);
+ protected abstract void serializeMpUnreachNlri(final DestinationType dstType, final ByteBuf byteAggregator);
public abstract void extractSpecificFlowspec(final ChoiceNode fsType, final FlowspecBuilder fsBuilder);
protected abstract void stringSpecificFSNlriType(final FlowspecType value, final StringBuilder buffer);
- abstract DestinationType createWithdrawnDestinationType(final List<Flowspec> dst, @Nullable final PathId pathId);
+ /**
+ * Create withdrawn destination type
+ *
+ * @param nlriFields a list of NLRI fields to be included in the destination type
+ * @param pathId associated path id with given NLRI
+ * @return created destination type
+ */
+ protected abstract DestinationType createWithdrawnDestinationType(@Nonnull final Object[] nlriFields, @Nullable final PathId pathId);
- abstract DestinationType createAdvertizedRoutesDestinationType(final List<Flowspec> dst, @Nullable final PathId pathId);
+ /**
+ * Create advertized destination type
+ *
+ * @param nlriFields a list of NLRI fields to be included in the destination type
+ * @param pathId associated path id with given NLRI
+ * @return created destination type
+ */
+ protected abstract DestinationType createAdvertizedRoutesDestinationType(@Nonnull final Object[] nlriFields, @Nullable final PathId pathId);
@Override
public final void serializeAttribute(final DataObject attribute, final ByteBuf byteAggregator) {
final Attributes pathAttributes = (Attributes) attribute;
final Attributes1 pathAttributes1 = pathAttributes.getAugmentation(Attributes1.class);
final Attributes2 pathAttributes2 = pathAttributes.getAugmentation(Attributes2.class);
- serializeMpReachNlri(pathAttributes1, byteAggregator);
- serializeMpUnreachNlri(pathAttributes2, byteAggregator);
+
+ if (pathAttributes1 != null) {
+ final AdvertizedRoutes routes = (pathAttributes1.getMpReachNlri()).getAdvertizedRoutes();
+ if (routes != null) {
+ serializeMpReachNlri(routes.getDestinationType(), byteAggregator);
+ }
+ }
+
+ if (pathAttributes2 != null) {
+ final WithdrawnRoutes routes = pathAttributes2.getMpUnreachNlri().getWithdrawnRoutes();
+ if (routes != null) {
+ serializeMpUnreachNlri(routes.getDestinationType(), byteAggregator);
+ }
+ }
}
@Override
parseNlri(nlri, builder, null);
}
+ protected void serializeNlri(@Nonnull final Object[] nlriFields, @Nonnull final ByteBuf buffer) {
+ final List<Flowspec> flowspecList = (List<Flowspec>) nlriFields[0];
+ serializeNlri(flowspecList, buffer);
+ }
+
+ protected final void serializeNlri(final List<Flowspec> flowspecList, @Nonnull final ByteBuf buffer) {
+ if (flowspecList != null) {
+ for (final Flowspec flow : flowspecList) {
+ this.flowspecTypeRegistry.serializeFlowspecType(flow.getFlowspecType(), buffer);
+ }
+ }
+ }
+
/**
* Serializes Flowspec NLRI to ByteBuf.
- * @param flows flowspec NLRI to be serialized
+ *
+ * @param nlriFields NLRI fields to be serialized
* @param pathId
- * @param buffer where flowspec NLRI will be serialized
+ * @param buffer where flowspec NLRI will be serialized
*/
- public final void serializeNlri(final List<Flowspec> flows, final PathId pathId, final ByteBuf buffer) {
+ protected final void serializeNlri(@Nonnull final Object[] nlriFields, @Nullable final PathId pathId, @Nonnull final ByteBuf buffer) {
final ByteBuf nlriByteBuf = Unpooled.buffer();
PathIdUtil.writePathId(pathId, buffer);
- for (final Flowspec flow : flows) {
- this.flowspecTypeRegistry.serializeFlowspecType(flow.getFlowspecType(), nlriByteBuf);
- }
+ serializeNlri(nlriFields, nlriByteBuf);
+
Preconditions.checkState(nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH, "Maximum length of Flowspec NLRI reached.");
if (nlriByteBuf.readableBytes() <= MAX_NLRI_LENGTH_ONE_BYTE) {
buffer.writeByte(nlriByteBuf.readableBytes());
buffer.writeBytes(nlriByteBuf);
}
- public final String stringNlri(final DataContainerNode<?> flowspec) {
+ public String stringNlri(final DataContainerNode<?> flowspec) {
return stringNlri(extractFlowspec(flowspec));
}
public final List<Flowspec> extractFlowspec(final DataContainerNode<?> route) {
+ Preconditions.checkNotNull(route, "Cannot extract flowspec from null route.");
final List<Flowspec> fsList = new ArrayList<>();
final Optional<DataContainerChild<? extends PathArgument, ?>> flowspecs = route.getChild(FLOWSPEC_NID);
if (flowspecs.isPresent()) {
- for (final UnkeyedListEntryNode flowspec : ((UnkeyedListNode)flowspecs.get()).getValue()) {
+ for (final UnkeyedListEntryNode flowspec : ((UnkeyedListNode) flowspecs.get()).getValue()) {
final FlowspecBuilder fsBuilder = new FlowspecBuilder();
final Optional<DataContainerChild<?, ?>> flowspecType = flowspec.getChild(FLOWSPEC_TYPE_NID);
if (flowspecType.isPresent()) {
return new Fragment(data.contains(DO_NOT_VALUE), data.contains(FIRST_VALUE), data.contains(IS_A_VALUE), data.contains(LAST_VALUE));
}
- final String stringNlri(final List<Flowspec> flows) {
+ protected final String stringNlri(final List<Flowspec> flows) {
final StringBuilder buffer = new StringBuilder("all packets ");
final Joiner joiner = Joiner.on(FLOW_SEPARATOR);
- joiner.appendTo(buffer, Iterables.transform(flows, new Function<Flowspec, String>() {
- @Override
- public String apply(final Flowspec input) {
- return encodeFlow(input);
- }
- }));
+ joiner.appendTo(buffer, Iterables.transform(flows, fs -> encodeFlow(fs)));
return buffer.toString().replace(" ", " ");
}
* @param nlri byte representation of NLRI which will be parsed
* @return list of Flowspec
*/
- public final List<Flowspec> parseNlri(final ByteBuf nlri) throws BGPParsingException {
+ protected final List<Flowspec> parseNlriFlowspecList(@Nonnull final ByteBuf nlri) throws BGPParsingException {
if (!nlri.isReadable()) {
return null;
}
final List<Flowspec> fss = new ArrayList<>();
- // length field can be one or two bytes (if needed)
- // check the length of nlri to see how many bytes we can skip
- final int length = nlri.readableBytes();
- nlri.skipBytes(length > MAX_NLRI_LENGTH_ONE_BYTE ? NLRI_LENGTH_EXTENDED : NLRI_LENGTH);
-
while (nlri.isReadable()) {
final FlowspecBuilder builder = new FlowspecBuilder();
builder.setFlowspecType(this.flowspecTypeRegistry.parseFlowspecType(nlri));
return fss;
}
+ /**
+ * This step is used to verify the NLRI length we read from BGP message
+ *
+ * @param nlri
+ */
+ private static final void verifyNlriLength(@Nonnull final ByteBuf nlri) {
+ // length field can be one or two bytes (if needed)
+ // check the length of nlri to see how many bytes we can skip
+ int readableLength = nlri.readableBytes();
+ final int expectedLength; // read the length from field
+ if (readableLength > MAX_NLRI_LENGTH_ONE_BYTE) {
+ expectedLength = nlri.readUnsignedShort();
+ readableLength -= NLRI_LENGTH_EXTENDED; // deduct the two bytes of the NLRI length field
+ } else {
+ expectedLength = nlri.readUnsignedByte();
+ readableLength -= NLRI_LENGTH; // deduct the one byte of the NLRI length field
+ }
+ Preconditions.checkState(readableLength == expectedLength, "NLRI length read from message doesn't match. Length expected (read from NLRI) is %s, length readable is %s", expectedLength, readableLength);
+ }
+
+ /**
+ * Override this function to parse additional NLRI fields
+ *
+ * @param nlri NLRI buffer
+ * @return Parsed additional fields
+ */
+ @Nonnull
+ protected Object[] parseNlri(@Nonnull final ByteBuf nlri) throws BGPParsingException {
+ return new Object[] {parseNlriFlowspecList(nlri)};
+ }
@Override
- public void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpReachNlriBuilder builder, @Nullable final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+ public final void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpReachNlriBuilder builder, @Nullable final PeerSpecificParserConstraint constraint)
+ throws BGPParsingException {
if (!nlri.isReadable()) {
return;
}
final PathId pathId = readPathId(nlri, builder.getAfi(), builder.getSafi(), constraint);
- final List<Flowspec> dst = parseNlri(nlri);
- builder.setAdvertizedRoutes(new AdvertizedRoutesBuilder()
- .setDestinationType(createAdvertizedRoutesDestinationType(dst, pathId)).build());
+ verifyNlriLength(nlri);
+ final Object[] nlriFields = parseNlri(nlri);
+ builder.setAdvertizedRoutes(
+ new AdvertizedRoutesBuilder()
+ .setDestinationType(
+ createAdvertizedRoutesDestinationType(nlriFields, pathId)
+ ).build()
+ );
}
- private PathId readPathId(final ByteBuf nlri, final Class<? extends AddressFamily> afi, final Class<? extends SubsequentAddressFamily> safi,
+ @Nullable
+ protected final PathId readPathId(@Nonnull final ByteBuf nlri, final Class<? extends AddressFamily> afi, final Class<? extends SubsequentAddressFamily> safi,
final PeerSpecificParserConstraint constraint) {
if (MultiPathSupportUtil.isTableTypeSupported(constraint, new BgpTableTypeImpl(afi, safi))) {
return PathIdUtil.readPathId(nlri);
return null;
}
-
@Override
- public void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpUnreachNlriBuilder builder, @Nullable final PeerSpecificParserConstraint constraint) throws BGPParsingException {
+ public final void parseNlri(@Nonnull final ByteBuf nlri, @Nonnull final MpUnreachNlriBuilder builder, @Nullable final PeerSpecificParserConstraint constraint)
+ throws BGPParsingException {
if (!nlri.isReadable()) {
return;
}
final PathId pathId = readPathId(nlri, builder.getAfi(), builder.getSafi(), constraint);
- final List<Flowspec> dst = parseNlri(nlri);
- builder.setWithdrawnRoutes(new WithdrawnRoutesBuilder().setDestinationType(createWithdrawnDestinationType(dst, pathId)).build());
+ verifyNlriLength(nlri);
+ final Object[] nlriFields = parseNlri(nlri);
+ builder.setWithdrawnRoutes(
+ new WithdrawnRoutesBuilder()
+ .setDestinationType(
+ createWithdrawnDestinationType(nlriFields, pathId)
+ ).build()
+ );
}
}
+