Convert treat-as-withdraw update messages 49/78349/52
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 14 Dec 2018 00:35:40 +0000 (01:35 +0100)
committerRobert Varga <nite@hq.sk>
Fri, 14 Dec 2018 17:27:23 +0000 (17:27 +0000)
When we encounter a treat-as-withdraw error, we need to convert
the Update message to its withdraw equivalent.

JIRA: BGPCEP-359
Change-Id: I383571360f41a3cf874c55e13e6c4212f31522ff
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
Signed-off-by: Matej Perina <matej.perina@pantheon.tech>
bgp/extensions/linkstate/src/test/java/org/opendaylight/protocol/bgp/linkstate/ParserTest.java
bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/BGPActivator.java
bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/BGPUpdateMessageParser.java
bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/BGPParserTest.java

index d310fb7840e13f0a240db939e8881e38811e14c3..c58a82cf0db2d256a64e5a9b3313fe3570693267 100644 (file)
@@ -11,6 +11,7 @@ import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
@@ -30,6 +31,7 @@ import org.opendaylight.protocol.bgp.parser.BgpTableTypeImpl;
 import org.opendaylight.protocol.bgp.parser.impl.message.BGPUpdateMessageParser;
 import org.opendaylight.protocol.bgp.parser.spi.MessageRegistry;
 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
+import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
 import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
 import org.opendaylight.protocol.util.ByteArray;
 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.AsNumber;
@@ -109,7 +111,7 @@ public class ParserTest {
     @Before
     public void setUp() throws Exception {
         updateParser = new BGPUpdateMessageParser(ServiceLoaderBGPExtensionProviderContext
-            .getSingletonInstance().getAttributeRegistry());
+            .getSingletonInstance().getAttributeRegistry(), mock(NlriRegistry.class));
         for (int i = 1; i <= COUNTER; i++) {
             final String name = "/up" + i + ".bin";
             try (final InputStream is = ParserTest.class.getResourceAsStream(name)){
index 306c8362a3b027b38548d60fec34a38c7037d247..ce42a93b2c2a69faa33eb03679da6255c7e62b8a 100755 (executable)
@@ -257,7 +257,8 @@ public final class BGPActivator extends AbstractBGPExtensionProviderActivator {
         regs.add(context.registerMessageParser(BGPOpenMessageParser.TYPE, omp));
         regs.add(context.registerMessageSerializer(Open.class, omp));
 
-        final BGPUpdateMessageParser ump = new BGPUpdateMessageParser(context.getAttributeRegistry());
+        final BGPUpdateMessageParser ump = new BGPUpdateMessageParser(context.getAttributeRegistry(),
+            context.getNlriRegistry());
         regs.add(context.registerMessageParser(BGPUpdateMessageParser.TYPE, ump));
         regs.add(context.registerMessageSerializer(Update.class, ump));
 
index a331d742377215bed3bf03d23a474d7033f4c4f9..978851173f448ae1720de5f17dc06534d459ec90 100755 (executable)
@@ -30,6 +30,7 @@ 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.MultiPathSupportUtil;
+import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
 import org.opendaylight.protocol.bgp.parser.spi.ParsedAttributes;
 import org.opendaylight.protocol.bgp.parser.spi.PathIdUtil;
 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
@@ -41,10 +42,17 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.mess
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.Update;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.UpdateBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.Attributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.path.attributes.AttributesBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.Nlri;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.NlriBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.WithdrawnRoutes;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.update.message.WithdrawnRoutesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.Attributes2Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.graceful.restart.capability.TablesKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpUnreachNlri;
 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;
 import org.opendaylight.yangtools.yang.binding.Notification;
@@ -66,10 +74,13 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
 
     private static final int TOTAL_PATH_ATTR_LENGTH_SIZE = 2;
 
-    private final AttributeRegistry reg;
+    private final AttributeRegistry attrReg;
 
-    public BGPUpdateMessageParser(final AttributeRegistry reg) {
-        this.reg = requireNonNull(reg);
+    private final NlriRegistry nlriReg;
+
+    public BGPUpdateMessageParser(final AttributeRegistry attrReg, final NlriRegistry nlriReg) {
+        this.attrReg = requireNonNull(attrReg);
+        this.nlriReg = requireNonNull(nlriReg);
     }
 
     @Override
@@ -90,7 +101,7 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         }
         if (update.getAttributes() != null) {
             final ByteBuf pathAttributesBuf = Unpooled.buffer();
-            this.reg.serializeAttribute(update.getAttributes(), pathAttributesBuf);
+            this.attrReg.serializeAttribute(update.getAttributes(), pathAttributesBuf);
             messageBody.writeShort(pathAttributesBuf.writerIndex());
             messageBody.writeBytes(pathAttributesBuf);
         } else {
@@ -184,7 +195,6 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         }
 
         Update msg = builder.build();
-
         if (withdrawCauseOpt.isPresent()) {
             // Attempt to apply treat-as-withdraw
             msg = withdrawUpdate(msg, errorHandling, withdrawCauseOpt.get());
@@ -198,7 +208,7 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
     private ParsedAttributes parseAttributes(final ByteBuf buffer, final int totalPathAttrLength,
             final PeerSpecificParserConstraint constraint) throws BGPDocumentedException {
         try {
-            return reg.parseAttributes(buffer.readSlice(totalPathAttrLength), constraint);
+            return attrReg.parseAttributes(buffer.readSlice(totalPathAttrLength), constraint);
         } catch (final RuntimeException | BGPParsingException e) {
             // Catch everything else and turn it into a BGPDocumentedException
             throw new BGPDocumentedException("Could not parse BGP attributes.", BGPError.MALFORMED_ATTR_LIST, e);
@@ -254,7 +264,7 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
             "Well known mandatory attribute missing: %s", attrName);
     }
 
-    private static Update withdrawUpdate(final Update parsed, final RevisedErrorHandling errorHandling,
+    private Update withdrawUpdate(final Update parsed, final RevisedErrorHandling errorHandling,
             final BGPTreatAsWithdrawException withdrawCause) throws BGPDocumentedException {
         if (errorHandling == RevisedErrorHandling.NONE) {
             throw new BGPDocumentedException(withdrawCause);
@@ -276,8 +286,52 @@ public final class BGPUpdateMessageParser implements MessageParser, MessageSeria
         }
         builder.setWithdrawnRoutes(withdrawn);
 
-        // FIXME: BGPCEP-359: deal with MP_REACH-to-MP_UNREACH conversion
+        final Attributes attributes = parsed.getAttributes();
+        if (attributes != null) {
+            builder.setAttributes(withdrawAttributes(attributes, withdrawCause));
+        }
+
+        return builder.build();
+    }
+
+    private Attributes withdrawAttributes(final Attributes parsed,
+            final BGPTreatAsWithdrawException withdrawCause) throws BGPDocumentedException {
+        final AttributesBuilder builder = new AttributesBuilder();
+        final MpReachNlri mpReachNlri = getMpReach(parsed);
+        if (mpReachNlri == null) {
+            // No MP_REACH attribute, just reuse MP_UNREACH if it is present.
+            builder.addAugmentation(Attributes2.class, parsed.augmentation(Attributes2.class));
+            return builder.build();
+        }
+
+        final MpUnreachNlri unreachNlri = getMpUnreach(parsed);
+        if (unreachNlri != null) {
+            final TablesKey reachKey = new TablesKey(mpReachNlri.getAfi(), mpReachNlri.getSafi());
+            final TablesKey unreachKey = new TablesKey(unreachNlri.getAfi(), unreachNlri.getSafi());
+            if (!reachKey.equals(unreachKey)) {
+                LOG.warn("Unexpected mismatch between MP_REACH ({}) and MP_UNREACH ({})", reachKey, unreachKey,
+                    withdrawCause);
+                throw new BGPDocumentedException(withdrawCause);
+            }
+        }
+
+        final MpUnreachNlri converted = this.nlriReg.convertMpReachToMpUnReach(mpReachNlri, unreachNlri)
+                .orElseThrow(() -> {
+                    LOG.warn("Could not convert attributes {} to withdraw attributes", parsed, withdrawCause);
+                    return new BGPDocumentedException(withdrawCause);
+                });
 
+        builder.addAugmentation(Attributes2.class, new Attributes2Builder().setMpUnreachNlri(converted).build());
         return builder.build();
     }
+
+    private static MpReachNlri getMpReach(final Attributes attrs) {
+        final Attributes1 reachAttr = attrs.augmentation(Attributes1.class);
+        return reachAttr == null ? null : reachAttr.getMpReachNlri();
+    }
+
+    private static MpUnreachNlri getMpUnreach(final Attributes attrs) {
+        final Attributes2 unreachAttr = attrs.augmentation(Attributes2.class);
+        return unreachAttr == null ? null : unreachAttr.getMpUnreachNlri();
+    }
 }
index 957e3d1db5a5fa2099373dde3aad96ecd1331b04..d4f663e73a778615a551437baad458d1517c8119 100644 (file)
@@ -30,6 +30,7 @@ import org.opendaylight.protocol.bgp.parser.impl.message.BGPUpdateMessageParser;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.CommunityUtil;
 import org.opendaylight.protocol.bgp.parser.spi.MessageUtil;
 import org.opendaylight.protocol.bgp.parser.spi.MultiPathSupport;
+import org.opendaylight.protocol.bgp.parser.spi.NlriRegistry;
 import org.opendaylight.protocol.bgp.parser.spi.PeerSpecificParserConstraint;
 import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
 import org.opendaylight.protocol.bgp.util.HexDumpBGPFileParser;
@@ -93,7 +94,7 @@ public class BGPParserTest {
     @BeforeClass
     public static void setUp() throws Exception {
         updateParser = new BGPUpdateMessageParser(ServiceLoaderBGPExtensionProviderContext.getSingletonInstance()
-            .getAttributeRegistry());
+            .getAttributeRegistry(), mock(NlriRegistry.class));
         for (int i = 1; i <= COUNTER; i++) {
             final String name = "/up" + i + ".bin";
             try (InputStream is = BGPParserTest.class.getResourceAsStream(name)) {