Throw ThreatAsWithdrawException on missing mandatory attributes 46/78646/7
authorMatej Perina <matej.perina@pantheon.tech>
Tue, 11 Dec 2018 12:33:16 +0000 (13:33 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Fri, 14 Dec 2018 00:43:58 +0000 (01:43 +0100)
https://tools.ietf.org/html/rfc7606#section-3:
d.  If any of the well-known mandatory attributes are not present in
    an UPDATE message, then "treat-as-withdraw" MUST be used.

For this to work we need to update BGPTreatAsWithdrawException,
because RFC4271 requires us to report the attribute type.

JIRA: BGPCEP-359
Change-Id: I3f1a1d56a3cf1e7e1b84fe2f7a5933af95129b91
Signed-off-by: Matej Perina <matej.perina@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/AbstractBGPException.java [new file with mode: 0644]
bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/BGPDocumentedException.java
bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/BGPTreatAsWithdrawException.java
bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java
bgp/parser-spi/src/main/java/org/opendaylight/protocol/bgp/parser/spi/RevisedErrorHandling.java

diff --git a/bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/AbstractBGPException.java b/bgp/parser-api/src/main/java/org/opendaylight/protocol/bgp/parser/AbstractBGPException.java
new file mode 100644 (file)
index 0000000..3c63f51
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+/**
+ * Abstract class supporting common aspects of {@link BGPDocumentedException} and {@link BGPTreatAsWithdrawException}.
+ */
+public abstract class AbstractBGPException extends Exception {
+    private static final long serialVersionUID = 1L;
+    private static final byte @NonNull [] EMPTY = new byte[0];
+
+    private final @NonNull BGPError error;
+    private final byte[] data;
+
+    /**
+     * Used when an error occurred that is described in an RFC or a draft.
+     *
+     * @param message message bound with this exception
+     * @param error specific documented error
+     * @param data data associated with the error
+     * @param cause cause for the error
+     */
+    AbstractBGPException(final String message, final BGPError error, final byte[] data, final Exception cause) {
+        super(message, cause);
+        this.error = requireNonNull(error);
+        this.data = data == null || data.length == 0 ? null : data.clone();
+    }
+
+    /**
+     * Returns specific documented error.
+     *
+     * @return documented error
+     */
+    public final BGPError getError() {
+        return error;
+    }
+
+    /**
+     * Returns data associated with this error.
+     *
+     * @return byte array data
+     */
+    public final byte @NonNull [] getData() {
+        return data != null ? data.clone() : EMPTY;
+    }
+}
index a543e0f4c8a937c4248f8fd2fb04258da66391e9..bf22b237b2ec677a7e3d8136c1a8b439467ea1b3 100644 (file)
@@ -17,18 +17,10 @@ import org.slf4j.LoggerFactory;
  * There are several errors documented in RFC4271 or in draft, that have specific meaning for the BGP.
  * This exception is used, when any of those errors occurs.
  */
-public final class BGPDocumentedException extends Exception {
-
+public final class BGPDocumentedException extends AbstractBGPException {
     private static final long serialVersionUID = -6212702584439430736L;
-
     private static final Logger LOG = LoggerFactory.getLogger(BGPDocumentedException.class);
 
-    private static final byte[] EMPTY = new byte[0];
-
-    private final BGPError error;
-
-    private final byte[] data;
-
     /**
      * Used when an error occurred that is described in an RFC or a draft.
      *
@@ -80,28 +72,18 @@ public final class BGPDocumentedException extends Exception {
      */
     public BGPDocumentedException(final String message, final BGPError error, final byte[] data,
             final Exception cause) {
-        super(message, cause);
-        this.error = error;
-        this.data = data == null || data.length == 0 ? null : data.clone();
+        super(message, error, data, cause);
+        // FIXME: remove this error?
         LOG.error("Error = {}", error, this);
     }
 
     /**
-     * Returns specific documented error.
-     *
-     * @return documented error
-     */
-    public BGPError getError() {
-        return error;
-    }
-
-    /**
-     * Returns data associated with this error.
+     * Used when an error occurred that is described in an RFC or a draft.
      *
-     * @return byte array data
+     * @param cause cause for the error
      */
-    public byte[] getData() {
-        return data != null ? data.clone() : EMPTY;
+    public BGPDocumentedException(final BGPTreatAsWithdrawException cause) {
+        this(cause.getMessage(), cause.getError(), cause.getData(), cause);
     }
 
     public static BGPDocumentedException badMessageLength(final String message, final int length) {
index 06ad963e484ea62592c0d8056a78c8afd98c8799..a4483fd4af22e0b2b9a36735d12a72f7c93a6de3 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.protocol.bgp.parser;
 
-import static java.util.Objects.requireNonNull;
-
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 
@@ -21,28 +19,21 @@ import org.eclipse.jdt.annotation.Nullable;
  *
  * @author Robert Varga
  */
-public final class BGPTreatAsWithdrawException extends Exception {
+public final class BGPTreatAsWithdrawException extends AbstractBGPException {
     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);
+        this(error, (Exception) 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 BGPError getError() {
-        return error;
+    public BGPTreatAsWithdrawException(final @NonNull BGPError error, final byte[] data, final @NonNull String format,
+            final Object... args) {
+        super(String.format(format, args), error, data, null);
     }
 
-    // FIXME: remove this method, as it makes checkstyle unhappy
-    public @NonNull BGPDocumentedException toDocumentedException() {
-        return new BGPDocumentedException(getMessage(), error, this);
+    public BGPTreatAsWithdrawException(final @NonNull BGPError error, @Nullable final Exception cause,
+            final @NonNull String format, final Object... args) {
+        super(String.format(format, args), error, null, cause);
     }
 }
index a1c89c7e240f6472a07287827f97afd4cf5e5523..a4c166c1326a34f19d1df9aaba989e95d21d0a63 100755 (executable)
@@ -31,6 +31,7 @@ import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupportUtil;
 import org.opendaylight.protocol.bgp.parser.spi.ParsedAttributes;
 import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
+import org.opendaylight.protocol.bgp.parser.spi.RevisedErrorHandling;
 import org.opendaylight.protocol.util.ByteBufWriteUtil;
 import org.opendaylight.protocol.util.Ipv4Util;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
@@ -106,8 +107,8 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
     }
 
     /**
-     * Parse Update message from buffer.
-     * Calls {@link #checkMandatoryAttributesPresence(Update)} to check for presence of mandatory attributes.
+     * Parse Update message from buffer. Calls {@link #checkMandatoryAttributesPresence(Update, RevisedErrorHandling)}
+     * to check for presence of mandatory attributes.
      *
      * @param buffer Encoded BGP message in ByteBuf
      * @param messageLength Length of the BGP message
@@ -123,6 +124,7 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         final UpdateBuilder builder = new UpdateBuilder();
         final boolean isMultiPathSupported = MultiPathSupportUtil.isTableTypeSupported(constraint,
                 new BgpTableTypeImpl(Ipv4AddressFamily.class, UnicastSubsequentAddressFamily.class));
+        final RevisedErrorHandling errorHandling = RevisedErrorHandling.from(constraint);
 
         final int withdrawnRoutesLength = buffer.readUnsignedShort();
         if (withdrawnRoutesLength > 0) {
@@ -144,13 +146,13 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         if (withdrawnRoutesLength == 0 && totalPathAttrLength == 0) {
             return builder.build();
         }
-        final Optional<BGPTreatAsWithdrawException> withdrawCause;
+        Optional<BGPTreatAsWithdrawException> withdrawCauseOpt;
         if (totalPathAttrLength > 0) {
             final ParsedAttributes attributes = parseAttributes(buffer, totalPathAttrLength, constraint);
             builder.setAttributes(attributes.getAttributes());
-            withdrawCause = attributes.getWithdrawCause();
+            withdrawCauseOpt = attributes.getWithdrawCause();
         } else {
-            withdrawCause = Optional.empty();
+            withdrawCauseOpt = Optional.empty();
         }
         final List<Nlri> nlri = new ArrayList<>();
         while (buffer.isReadable()) {
@@ -164,12 +166,25 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         if (!nlri.isEmpty()) {
             builder.setNlri(nlri);
         }
+
+        try {
+            checkMandatoryAttributesPresence(builder.build(), errorHandling);
+        } catch (BGPTreatAsWithdrawException e) {
+            LOG.debug("Well-known mandatory attributes missing", e);
+            if (withdrawCauseOpt.isPresent()) {
+                final BGPTreatAsWithdrawException exception = withdrawCauseOpt.get();
+                exception.addSuppressed(e);
+                withdrawCauseOpt = Optional.of(exception);
+            } else {
+                withdrawCauseOpt = Optional.of(e);
+            }
+        }
+
         Update msg = builder.build();
-        checkMandatoryAttributesPresence(msg);
 
-        if (withdrawCause.isPresent()) {
+        if (withdrawCauseOpt.isPresent()) {
             // FIXME: BGPCEP-359: check if we can treat the message as withdraw and convert the message
-            throw withdrawCause.get().toDocumentedException();
+            throw new BGPDocumentedException(withdrawCauseOpt.get());
         }
 
         LOG.debug("BGP Update message was parsed {}.", msg);
@@ -191,32 +206,30 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
      * Check for presence of well known mandatory path attributes ORIGIN, AS_PATH and NEXT_HOP in Update message.
      *
      * @param message Update message
+     * @param errorHandling Error handling type
      */
-    private static void checkMandatoryAttributesPresence(final Update message) throws BGPDocumentedException {
+    private static void checkMandatoryAttributesPresence(final Update message,
+            final RevisedErrorHandling errorHandling) throws BGPDocumentedException, BGPTreatAsWithdrawException {
         requireNonNull(message, "Update message cannot be null");
 
         final Attributes attrs = message.getAttributes();
-
-        if (message.getNlri() != null) {
-            if (attrs == null || attrs.getCNextHop() == null) {
-                throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "NEXT_HOP",
-                        BGPError.WELL_KNOWN_ATTR_MISSING,
-                        new byte[] { NextHopAttributeParser.TYPE });
-            }
+        if (message.getNlri() != null && (attrs == null || attrs.getCNextHop() == null)) {
+            throw reportMissingAttribute(errorHandling, "NEXT_HOP", NextHopAttributeParser.TYPE);
         }
 
         if (MessageUtil.isAnyNlriPresent(message)) {
             if (attrs == null || attrs.getOrigin() == null) {
-                throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "ORIGIN",
-                        BGPError.WELL_KNOWN_ATTR_MISSING,
-                        new byte[] { OriginAttributeParser.TYPE });
+                throw reportMissingAttribute(errorHandling, "ORIGIN", OriginAttributeParser.TYPE);
             }
-
             if (attrs.getAsPath() == null) {
-                throw new BGPDocumentedException(BGPError.MANDATORY_ATTR_MISSING_MSG + "AS_PATH",
-                        BGPError.WELL_KNOWN_ATTR_MISSING,
-                        new byte[] { AsPathAttributeParser.TYPE });
+                throw reportMissingAttribute(errorHandling, "AS_PATH", AsPathAttributeParser.TYPE);
             }
         }
     }
+
+    private static BGPDocumentedException reportMissingAttribute(final RevisedErrorHandling errorHandling,
+            final String attrName, final int attrType) throws BGPDocumentedException, BGPTreatAsWithdrawException {
+        return errorHandling.reportError(BGPError.WELL_KNOWN_ATTR_MISSING, new byte[] { (byte) attrType },
+            "Well known mandatory attribute missing: %s", attrName);
+    }
 }
index 537a2111031e889fac0e867b4fdc2704c728b5f4..cc29651311854cd1041d3fc1f2b70194e9477649 100644 (file)
@@ -80,6 +80,22 @@ public enum RevisedErrorHandling {
      */
     public BGPDocumentedException reportError(final BGPError error, final String format, final Object... args)
             throws BGPDocumentedException, BGPTreatAsWithdrawException {
-        throw reportError(error, null, format, args);
+        throw reportError(error, (Exception) null, 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 byte[] data, final String format,
+            final Object... args) throws BGPDocumentedException, BGPTreatAsWithdrawException {
+        throw new BGPTreatAsWithdrawException(error, data, format, args);
     }
 }