Add Long-lived Graceful restart capabilities 68/78468/1
authorRobert Varga <robert.varga@pantheon.tech>
Wed, 5 Dec 2018 16:12:00 +0000 (17:12 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Wed, 5 Dec 2018 16:53:40 +0000 (17:53 +0100)
This adds the definition of protocol-level capabilities
for draft-uttaro-idr-bgp-persistence-04.

Change-Id: I9885f65615678d8f01e238b0f2aa367de61bb60c
JIRA: BGPCEP-495
Signed-off-by: Matej Perina <matej.perina@pantheon.tech>
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
bgp/parser-api/src/main/yang/bgp-multiprotocol.yang
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/open/LlGracefulCapabilityHandler.java [new file with mode: 0644]
bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/LlGracefulCapabilityHandlerTest.java [new file with mode: 0644]

index 71705a654338c6031e277a61dc37b506a7e84d2f..da4e59d4ee4412e118a4644b7209c8ee1a414395 100644 (file)
@@ -117,6 +117,30 @@ module bgp-multiprotocol {
                 }
             }
         }
+        container ll-graceful-restart-capability {
+            reference "https://tools.ietf.org/html/draft-uttaro-idr-bgp-persistence-04";
+
+            list tables {
+                uses bgp-table-type;
+                key "afi safi";
+
+                leaf afi-flags {
+                    type bits {
+                        bit forwarding-state {
+                            position 0;
+                        }
+                    }
+                    mandatory true;
+                }
+                leaf long-live-stale-time {
+                    type uint32 {
+                        range 0..16777215;
+                    }
+                    units seconds;
+                    default 0;
+                }
+            }
+        }
         container add-path-capability {
             reference "http://tools.ietf.org/html/draft-ietf-idr-add-paths-13#section-4";
             list address-families {
index a7c7114efc50068243ea44ee95d5606423a8c853..75f3ea1cc5f5c8a0360e77460a95ef15fa9356ff 100755 (executable)
@@ -19,6 +19,7 @@ import org.opendaylight.protocol.bgp.parser.impl.message.open.As4CapabilityHandl
 import org.opendaylight.protocol.bgp.parser.impl.message.open.BgpExtendedMessageCapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.CapabilityParameterParser;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.GracefulCapabilityHandler;
+import org.opendaylight.protocol.bgp.parser.impl.message.open.LlGracefulCapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.MultiProtocolCapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.open.RouteRefreshCapabilityHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.AS4AggregatorAttributeParser;
@@ -46,11 +47,11 @@ import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communi
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.OpaqueEcHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.RouteOriginAsTwoOctetEcHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.RouteOriginIpv4EcHandler;
-import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.four.octect.as.specific.SourceAS4OctectHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.SourceASHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.VrfRouteImportHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.four.octect.as.specific.Generic4OctASEcHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.four.octect.as.specific.RouteOrigin4OctectASEcHandler;
+import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.four.octect.as.specific.SourceAS4OctectHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.route.target.RouteTarget4OctectASEcHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.route.target.RouteTargetAsTwoOctetEcHandler;
 import org.opendaylight.protocol.bgp.parser.impl.message.update.extended.communities.route.target.RouteTargetIpv4EcHandler;
@@ -82,6 +83,7 @@ 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.multiprotocol.rev180329.RouteRefresh;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.AddPathCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.GracefulRestartCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.LlGracefulRestartCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.MultiprotocolCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.RouteRefreshCapability;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.update.attributes.MpReachNlri;
@@ -157,6 +159,10 @@ public final class BGPActivator extends AbstractBGPExtensionProviderActivator {
         regs.add(context.registerCapabilitySerializer(GracefulRestartCapability.class, grace));
         regs.add(context.registerCapabilityParser(GracefulCapabilityHandler.CODE, grace));
 
+        final LlGracefulCapabilityHandler llgrace = new LlGracefulCapabilityHandler(afiReg, safiReg);
+        regs.add(context.registerCapabilitySerializer(LlGracefulRestartCapability.class, llgrace));
+        regs.add(context.registerCapabilityParser(LlGracefulCapabilityHandler.CODE, llgrace));
+
         final CapabilityParameterParser cpp = new CapabilityParameterParser(context.getCapabilityRegistry());
         regs.add(context.registerParameterParser(CapabilityParameterParser.TYPE, cpp));
         regs.add(context.registerParameterSerializer(BgpParameters.class, cpp));
diff --git a/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/open/LlGracefulCapabilityHandler.java b/bgp/parser-impl/src/main/java/org/opendaylight/protocol/bgp/parser/impl/message/open/LlGracefulCapabilityHandler.java
new file mode 100644 (file)
index 0000000..fb32858
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * 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.impl.message.open;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.protocol.bgp.parser.spi.AddressFamilyRegistry;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilityParser;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilitySerializer;
+import org.opendaylight.protocol.bgp.parser.spi.CapabilityUtil;
+import org.opendaylight.protocol.bgp.parser.spi.SubsequentAddressFamilyRegistry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.optional.capabilities.CParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.CParameters1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.CParameters1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.LlGracefulRestartCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.LlGracefulRestartCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.ll.graceful.restart.capability.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.ll.graceful.restart.capability.TablesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class LlGracefulCapabilityHandler implements CapabilityParser, CapabilitySerializer {
+    public static final int CODE = 71;
+
+    private static final Logger LOG = LoggerFactory.getLogger(LlGracefulCapabilityHandler.class);
+
+    // size of AFI (16 bits) and SAFI (8bits) fields in bytes
+    private static final int AFI_SAFI_SIZE = 3;
+
+    private static final int AFI_FLAGS_SIZE = 1;
+    private static final int STALE_TIME_SIZE = 3;
+
+    private static final int PER_TABLE_SIZE = AFI_SAFI_SIZE + AFI_FLAGS_SIZE + STALE_TIME_SIZE;
+
+    private static final int MAX_STALE_TIME = 16777215;
+    private static final byte AFI_FLAG_FORWARDING_STATE = (byte) 0x80;
+
+    private final AddressFamilyRegistry afiReg;
+    private final SubsequentAddressFamilyRegistry safiReg;
+
+    public LlGracefulCapabilityHandler(final AddressFamilyRegistry afiReg,
+                                       final SubsequentAddressFamilyRegistry safiReg) {
+        this.afiReg = requireNonNull(afiReg);
+        this.safiReg = requireNonNull(safiReg);
+    }
+
+    @Override
+    public CParameters parseCapability(final ByteBuf buffer) {
+        final List<Tables> tables = new ArrayList<>();
+
+        while (buffer.isReadable()) {
+            final short afival = buffer.readShort();
+            final Class<? extends AddressFamily> afi = this.afiReg.classForFamily(afival);
+            if (afi == null) {
+                LOG.debug("Ignoring GR capability for unknown address family {}", afival);
+                buffer.skipBytes(PER_TABLE_SIZE - 2);
+                continue;
+            }
+
+            final byte safival = buffer.readByte();
+            final Class<? extends SubsequentAddressFamily> safi = this.safiReg.classForFamily(safival);
+            if (safi == null) {
+                LOG.debug("Ignoring GR capability for unknown subsequent address family {}", safival);
+                buffer.skipBytes(1);
+                continue;
+            }
+
+            final byte afiFlags = buffer.readByte();
+            final int staleTime = buffer.readUnsignedMedium();
+            final Tables table = new TablesBuilder()
+                    .setAfi(afi)
+                    .setSafi(safi)
+                    .setAfiFlags(new Tables.AfiFlags(Boolean.valueOf(afiFlags == AFI_FLAG_FORWARDING_STATE)))
+                    .setLongLiveStaleTime(Long.valueOf(staleTime))
+                    .build();
+            tables.add(table);
+        }
+        return new CParametersBuilder()
+                .addAugmentation(CParameters1.class, new CParameters1Builder()
+                        .setLlGracefulRestartCapability(new LlGracefulRestartCapabilityBuilder()
+                                .setTables(tables)
+                                .build())
+                        .build())
+                .build();
+    }
+
+    @Override
+    public void serializeCapability(final CParameters capability, final ByteBuf byteAggregator) {
+        final CParameters1 aug = capability.augmentation(CParameters1.class);
+        if (aug != null) {
+            final LlGracefulRestartCapability cap = aug.getLlGracefulRestartCapability();
+            if (cap != null) {
+                CapabilityUtil.formatCapability(CODE, serializeCapability(cap), byteAggregator);
+            }
+        }
+    }
+
+    private ByteBuf serializeCapability(final LlGracefulRestartCapability cap) {
+        final List<Tables> tables = cap.getTables();
+        if (tables == null || tables.isEmpty()) {
+            return Unpooled.EMPTY_BUFFER;
+        }
+
+        final ByteBuf buffer = Unpooled.buffer(PER_TABLE_SIZE * tables.size());
+        for (Tables table : tables) {
+            final Class<? extends AddressFamily> afi = table.getAfi();
+            final Class<? extends SubsequentAddressFamily> safi = table.getSafi();
+            final Integer afival = this.afiReg.numberForClass(afi);
+            checkArgument(afival != null, "Unhandled address family %s", afi);
+            buffer.writeShort(afival);
+            final Integer safival = this.safiReg.numberForClass(safi);
+            checkArgument(safival != null, "Unhandled subsequent address family %s", safi);
+            buffer.writeByte(safival);
+            if (table.getAfiFlags() != null && table.getAfiFlags().isForwardingState()) {
+                buffer.writeByte(AFI_FLAG_FORWARDING_STATE);
+            } else {
+                buffer.writeZero(1);
+            }
+            final Long staleTime = table.getLongLiveStaleTime();
+            final int timeval = staleTime != null ? staleTime.intValue() : 0;
+            checkArgument(timeval >= 0 && timeval <= MAX_STALE_TIME, "Restart time is %s", staleTime);
+            buffer.writeMedium(timeval);
+        }
+        return buffer;
+    }
+}
diff --git a/bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/LlGracefulCapabilityHandlerTest.java b/bgp/parser-impl/src/test/java/org/opendaylight/protocol/bgp/parser/impl/LlGracefulCapabilityHandlerTest.java
new file mode 100644 (file)
index 0000000..2ae99f6
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * 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.impl;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import java.util.Arrays;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
+import org.opendaylight.protocol.bgp.parser.BGPParsingException;
+import org.opendaylight.protocol.bgp.parser.impl.message.open.LlGracefulCapabilityHandler;
+import org.opendaylight.protocol.bgp.parser.spi.BGPExtensionProviderContext;
+import org.opendaylight.protocol.bgp.parser.spi.pojo.ServiceLoaderBGPExtensionProviderContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.optional.capabilities.CParameters;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.message.rev180329.open.message.bgp.parameters.optional.capabilities.CParametersBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.CParameters1;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.CParameters1Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.LlGracefulRestartCapability;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.LlGracefulRestartCapabilityBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.ll.graceful.restart.capability.Tables;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.multiprotocol.rev180329.mp.capabilities.ll.graceful.restart.capability.TablesBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.AddressFamily;
+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.Ipv6AddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.SubsequentAddressFamily;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.bgp.types.rev180329.UnicastSubsequentAddressFamily;
+
+public class LlGracefulCapabilityHandlerTest {
+
+    private LlGracefulCapabilityHandler handler;
+
+    @Before
+    public void setUp() {
+        final BGPExtensionProviderContext ctx = ServiceLoaderBGPExtensionProviderContext.getSingletonInstance();
+        this.handler = new LlGracefulCapabilityHandler(
+                ctx.getAddressFamilyRegistry(), ctx.getSubsequentAddressFamilyRegistry());
+    }
+
+    @Test
+    public void testLongLivedGracefulCapabilityHandler() throws BGPParsingException, BGPDocumentedException {
+
+        final byte[] capaBytes = {
+                //header
+                (byte) 0x47, (byte) 0x0e,
+                // Ipv4 Unicast, afiFlags = false, timer = 10
+                (byte) 0x00, (byte) 0x01, (byte) 0x01,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x0a,
+                // Ipv6 Unicast, afiFlags = true, timer = 160
+                (byte) 0x00, (byte) 0x02, (byte) 0x01,
+                (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0xa0
+        };
+
+        final LlGracefulRestartCapability capability = new LlGracefulRestartCapabilityBuilder()
+                .setTables(Arrays.asList(new TablesBuilder()
+                                .setAfi(Ipv4AddressFamily.class)
+                                .setSafi(UnicastSubsequentAddressFamily.class)
+                                .setAfiFlags(new Tables.AfiFlags(false))
+                                .setLongLiveStaleTime(10L)
+                                .build(),
+                        new TablesBuilder()
+                                .setAfi(Ipv6AddressFamily.class)
+                                .setSafi(UnicastSubsequentAddressFamily.class)
+                                .setAfiFlags(new Tables.AfiFlags(true))
+                                .setLongLiveStaleTime(160L)
+                                .build())
+                ).build();
+
+        final CParameters cParameters = new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setLlGracefulRestartCapability(capability).build()).build();
+        final ByteBuf buffer = Unpooled.buffer(capaBytes.length);
+        this.handler.serializeCapability(cParameters, buffer);
+
+        Assert.assertArrayEquals(capaBytes, buffer.array());
+        Assert.assertEquals(cParameters, this.handler.parseCapability(Unpooled.wrappedBuffer(capaBytes)
+                .slice(2, capaBytes.length - 2)));
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testUnsupportedAfi() {
+        final LlGracefulRestartCapability capability = new LlGracefulRestartCapabilityBuilder()
+                .setTables(Arrays.asList(new TablesBuilder()
+                                .setAfi(AddressFamily.class)
+                                .setSafi(UnicastSubsequentAddressFamily.class)
+                                .setAfiFlags(new Tables.AfiFlags(false))
+                                .setLongLiveStaleTime(10L)
+                                .build())).build();
+
+        final CParameters cParameters = new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setLlGracefulRestartCapability(capability).build()).build();
+        final ByteBuf buffer = Unpooled.buffer();
+        this.handler.serializeCapability(cParameters, buffer);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testUnsupportedSafi() {
+        final LlGracefulRestartCapability capability = new LlGracefulRestartCapabilityBuilder()
+                .setTables(Arrays.asList(new TablesBuilder()
+                        .setAfi(Ipv4AddressFamily.class)
+                        .setSafi(SubsequentAddressFamily.class)
+                        .setAfiFlags(new Tables.AfiFlags(false))
+                        .setLongLiveStaleTime(10L)
+                        .build())).build();
+
+        final CParameters cParameters = new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setLlGracefulRestartCapability(capability).build()).build();
+        final ByteBuf buffer = Unpooled.buffer();
+        this.handler.serializeCapability(cParameters, buffer);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testMinStaleTime() {
+        final LlGracefulRestartCapability capability = new LlGracefulRestartCapabilityBuilder()
+                .setTables(Arrays.asList(new TablesBuilder()
+                                .setAfi(Ipv4AddressFamily.class)
+                                .setSafi(UnicastSubsequentAddressFamily.class)
+                                .setAfiFlags(new Tables.AfiFlags(false))
+                                .setLongLiveStaleTime(-1L)
+                                .build())).build();
+
+        final CParameters cParameters = new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setLlGracefulRestartCapability(capability).build()).build();
+        final ByteBuf buffer = Unpooled.buffer();
+        this.handler.serializeCapability(cParameters, buffer);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testMaxStaleTime() {
+
+        final LlGracefulRestartCapability capability = new LlGracefulRestartCapabilityBuilder()
+                .setTables(Arrays.asList(new TablesBuilder()
+                        .setAfi(Ipv4AddressFamily.class)
+                        .setSafi(UnicastSubsequentAddressFamily.class)
+                        .setAfiFlags(new Tables.AfiFlags(false))
+                        .setLongLiveStaleTime(16777216L)
+                        .build())).build();
+
+        final CParameters cParameters = new CParametersBuilder().addAugmentation(CParameters1.class,
+                new CParameters1Builder().setLlGracefulRestartCapability(capability).build()).build();
+        final ByteBuf buffer = Unpooled.buffer();
+        this.handler.serializeCapability(cParameters, buffer);
+    }
+}