--- /dev/null
+/*
+ * Copyright (c) 2018 AT&T Intellectual Property. 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;
+
+import static java.util.Objects.requireNonNull;
+
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+
+/**
+ * An exception thrown when the parsing of an attribute results in treat-as-withdraw being applied to the UPDATE
+ * message, as per the rules in RFC7606 and related documents.
+ *
+ * <p>
+ * This exception must not be thrown when Revised Error Handling procedures are not in effect.
+ *
+ * @author Robert Varga
+ */
+public final class BGPTreatAsWithdrawException extends Exception {
+ private static final long serialVersionUID = 1L;
+
+ private final @NonNull BGPError error;
+
+ public BGPTreatAsWithdrawException(final @NonNull BGPError error, final @NonNull String format,
+ final Object... args) {
+ this(error, null, format, args);
+ }
+
+ public BGPTreatAsWithdrawException(final @NonNull BGPError error, @Nullable final Exception cause,
+ final @NonNull String format, final Object... args) {
+ super(String.format(format, args), cause);
+ this.error = requireNonNull(error);
+ }
+
+ public @NonNull BGPDocumentedException toDocumentedException() {
+ return new BGPDocumentedException(getMessage(), error, this);
+ }
+}
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.BGPTreatAsWithdrawException;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
/**
* @param builder Path attributes builder. Guaranteed to contain all valid attributes whose type is numerically
* lower than this attribute's type.
* @param constraint Peer specific constraints, may be null
+ * @throws BGPDocumentedException when an irrecoverable error occurred which has a {@link BGPError} assigned
+ * @throws BGPParsingException when a general unspecified parsing error occurs.
*/
void parseAttribute(@Nonnull ByteBuf buffer, @Nonnull AttributesBuilder builder,
@Nullable PeerSpecificParserConstraint constraint) throws BGPDocumentedException, BGPParsingException;
+ /**
+ * Parses attribute from ByteBuf buffer with the specified {@link RevisedErrorHandling}. Default implementation
+ * ignores error handling and defers to
+ * {@link #parseAttribute(ByteBuf, AttributesBuilder, PeerSpecificParserConstraint)}.
+ *
+ * @param buffer Encoded attribute body in ByteBuf.
+ * @param builder Path attributes builder. Guaranteed to contain all valid attributes whose type is numerically
+ * lower than this attribute's type.
+ * @param errorHandling RFC7606 error handling type
+ * @param constraint Peer specific constraints, may be null
+ * @throws BGPDocumentedException when an irrecoverable error occurred which has a {@link BGPError} assigned
+ * @throws BGPParsingException when a general unspecified parsing error occurs.
+ * @throws BGPTreatAsWithdrawException when parsing according to revised error handling indicates the
+ * message should be treated as withdraw.
+ */
+ default void parseAttribute(@Nonnull final ByteBuf buffer, @Nonnull final AttributesBuilder builder,
+ @Nonnull final RevisedErrorHandling errorHandling, @Nullable final PeerSpecificParserConstraint constraint)
+ throws BGPDocumentedException, BGPParsingException, BGPTreatAsWithdrawException {
+ parseAttribute(buffer, builder, constraint);
+ }
+
/**
* Determine whether a duplicate attribute should be ignored or {@link BGPError#MALFORMED_ATTR_LIST} should be
* raised. This is useful for MP_REACH/MP_UNREACH attributes, which need to emit a Notification under RFC7606
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPError;
+import org.opendaylight.protocol.bgp.parser.BGPTreatAsWithdrawException;
/**
* Enumeration of possible treatments an UPDATE message and attributes can get based on the configuration of a peer.
/**
* Do not use RFC7606 Revised Error Handling.
*/
- NONE,
+ NONE {
+ @Override
+ public BGPDocumentedException reportError(final BGPError error, final @Nullable Exception cause,
+ final String format, final Object... args) throws BGPDocumentedException {
+ throw new BGPDocumentedException(String.format(format, args), error, cause);
+ }
+ },
/**
* Use RFC7606 Revised Error Handling, the peer is an internal neighbor.
*/
return constraint == null ? NONE : constraint.getPeerConstraint(RevisedErrorHandlingSupport.class)
.map(support -> support.isExternalPeer() ? EXTERNAL : INTERNAL).orElse(NONE);
}
+
+ /**
+ * Report a failure to parse an attribute resulting either in treat-as-withdraw if RFC7606 is in effect, or
+ * connection teardown if it is not.
+ *
+ * @param error {@link BGPError} to report in case of a session teardown
+ * @param cause Parsing failure cause
+ * @param format Message format string
+ * @param args Message format arguments
+ * @return This method does not return
+ * @throws BGPTreatAsWithdrawException if Revised Error Handling is in effect
+ * @throws BGPDocumentedException if Revised Error Handling is in not effect
+ */
+ public BGPDocumentedException reportError(final BGPError error, final @Nullable Exception cause,
+ final String format, final Object... args) throws BGPDocumentedException, BGPTreatAsWithdrawException {
+ throw new BGPTreatAsWithdrawException(error, cause, format, args);
+ }
+
+ /**
+ * Report a failure to parse an attribute resulting either in treat-as-withdraw if RFC7606 is in effect, or
+ * connection teardown if it is not.
+ *
+ * @param error {@link BGPError} to report in case of a session teardown
+ * @param format Message format string
+ * @param args Message format arguments
+ * @return This method does not return
+ * @throws BGPTreatAsWithdrawException if Revised Error Handling is in effect
+ * @throws BGPDocumentedException if Revised Error Handling is in not effect
+ */
+ public BGPDocumentedException reportError(final BGPError error, final String format, final Object... args)
+ throws BGPDocumentedException, BGPTreatAsWithdrawException {
+ throw reportError(error, null, format, args);
+ }
}
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.BGPTreatAsWithdrawException;
import org.opendaylight.protocol.bgp.parser.spi.AttributeParser;
import org.opendaylight.protocol.bgp.parser.spi.AttributeRegistry;
import org.opendaylight.protocol.bgp.parser.spi.AttributeSerializer;
* TreeMap guarantees that we will be invoking the parser in the order
* of increasing attribute type.
*/
+ // We may have multiple attribute errors, each specifying a withdraw. We need to finish parsing the message
+ // all attributes before we can decide whether we can discard attributes, or whether we need to terminate
+ // the session.
final AttributesBuilder builder = new AttributesBuilder();
- for (final Entry<Integer, RawAttribute> e : attributes.entrySet()) {
- LOG.debug("Parsing attribute type {}", e.getKey());
-
- final RawAttribute a = e.getValue();
- a.parser.parseAttribute(a.buffer, builder, constraint);
+ BGPTreatAsWithdrawException withdrawCause = null;
+ for (final Entry<Integer, RawAttribute> entry : attributes.entrySet()) {
+ LOG.debug("Parsing attribute type {}", entry.getKey());
+
+ final RawAttribute a = entry.getValue();
+ try {
+ a.parser.parseAttribute(a.buffer, builder, errorHandling, constraint);
+ } catch (BGPTreatAsWithdrawException e) {
+ LOG.info("Attribute {} indicated treat-as-withdraw", entry.getKey(), e);
+ if (withdrawCause == null) {
+ withdrawCause = e;
+ } else {
+ withdrawCause.addSuppressed(e);
+ }
+ }
}
builder.setUnrecognizedAttributes(this.unrecognizedAttributes);
+
+ // FIXME: BGPCEP-359 report withdrawCause upstream, so it can be handled properly
+ if (withdrawCause != null) {
+ throw withdrawCause.toDocumentedException();
+ }
+
return builder.build();
}
import org.mockito.MockitoAnnotations;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.BGPTreatAsWithdrawException;
import org.opendaylight.protocol.bgp.parser.spi.AbstractBGPExtensionProviderActivator;
import org.opendaylight.protocol.bgp.parser.spi.AttributeParser;
import org.opendaylight.protocol.bgp.parser.spi.AttributeSerializer;
import org.opendaylight.protocol.bgp.parser.spi.ParameterParser;
import org.opendaylight.protocol.bgp.parser.spi.ParameterSerializer;
import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
+import org.opendaylight.protocol.bgp.parser.spi.RevisedErrorHandling;
import org.opendaylight.protocol.bgp.parser.spi.extended.community.ExtendedCommunityParser;
import org.opendaylight.protocol.bgp.parser.spi.extended.community.ExtendedCommunitySerializer;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
MockitoAnnotations.initMocks(this);
try {
Mockito.doNothing().when(this.attrParser).parseAttribute(any(ByteBuf.class), any(AttributesBuilder.class),
- any(PeerSpecificParserConstraint.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));
doReturn(EMPTY).when(this.attrSerializer).toString();
Mockito.doNothing().when(this.nlriParser).parseNlri(any(ByteBuf.class), any(MpReachNlriBuilder.class), any());
doReturn(EMPTY).when(this.nlriParser).toString();
- } catch (BGPDocumentedException | BGPParsingException e) {
+ } catch (BGPDocumentedException | BGPParsingException | BGPTreatAsWithdrawException e) {
Assert.fail();
}
}
import org.mockito.Mockito;
import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.BGPTreatAsWithdrawException;
import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
import org.opendaylight.protocol.bgp.parser.spi.AttributeRegistry;
import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext;
import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
import org.opendaylight.protocol.bgp.parser.spi.ParameterRegistry;
import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
+import org.opendaylight.protocol.bgp.parser.spi.RevisedErrorHandling;
import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.BgpParameters;
}
@Test
- public void testSimpleAttribute() throws BGPDocumentedException, BGPParsingException {
+ public void testSimpleAttribute() throws BGPDocumentedException, BGPParsingException, BGPTreatAsWithdrawException {
final AttributeRegistry attrReg = this.ctx.getAttributeRegistry();
final byte[] attributeBytes = {
0x00, 0x00, 0x00
attrReg.serializeAttribute(mock(Attributes.class), byteAggregator);
attrReg.parseAttributes(Unpooled.wrappedBuffer(attributeBytes), CONSTRAINT);
verify(this.activator.attrParser, times(1)).parseAttribute(any(ByteBuf.class), any(AttributesBuilder.class),
- any(PeerSpecificParserConstraint.class));
+ any(RevisedErrorHandling.class), any(PeerSpecificParserConstraint.class));
verify(this.activator.attrSerializer, times(1)).serializeAttribute(any(Attributes.class), any(ByteBuf.class));
}