summaryrefslogtreecommitdiffstats
path: root/mpls
diff options
context:
space:
mode:
authorMarek Gradzki <mgradzki@cisco.com>2017-11-22 17:56:52 +0100
committerMarek Gradzki <mgradzki@cisco.com>2017-11-23 12:17:34 +0000
commit82d2da4853410c0e7c00f383c01760cc7f26f75e (patch)
tree2a5fdac137267d8a657eb6325a46909b8c78a731 /mpls
parent1715b92455356a52e01ff5c4f2a42fe5336b9088 (diff)
HC2VPP-259: add support for mpls swap-and-forward
Limitations: - only IPv4 next hop address - swap is supported only for the last label in the stack - only single outgoing label is supported. Change-Id: I9479e3d322561ed94683bd206e7e7852250d788c Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Diffstat (limited to 'mpls')
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/ImposeAndForwardWriter.java100
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/Ipv4LookupWriter.java29
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsInSegmentTranslator.java38
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsLookupWriter.java29
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsSwapWriter.java93
-rw-r--r--mpls/impl/src/main/java/io/fd/hc2vpp/mpls/StaticLspCustomizer.java5
-rw-r--r--mpls/impl/src/test/java/io/fd/hc2vpp/mpls/ImposeAndForwardTest.java8
-rw-r--r--mpls/impl/src/test/java/io/fd/hc2vpp/mpls/MplsInterfaceCustomizerTest.java5
-rw-r--r--mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndIpv4LookupTest.java13
-rw-r--r--mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndMplsLookupTest.java13
-rw-r--r--mpls/impl/src/test/java/io/fd/hc2vpp/mpls/SwapAndForwardTest.java123
11 files changed, 347 insertions, 109 deletions
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/ImposeAndForwardWriter.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/ImposeAndForwardWriter.java
index b7ac96735..14053b63d 100644
--- a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/ImposeAndForwardWriter.java
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/ImposeAndForwardWriter.java
@@ -27,11 +27,13 @@ import io.fd.vpp.jvpp.core.future.FutureJVppCore;
import java.util.List;
import javax.annotation.Nonnull;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig.Operation;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp.Config;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.InSegment;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.OutSegment;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.Type;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.type.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.out.segment.PathList;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.out.segment.SimplePath;
@@ -60,64 +62,80 @@ final class ImposeAndForwardWriter implements LspWriter, Ipv4Translator {
final Config config = data.getConfig();
final IpAddDelRoute request = new IpAddDelRoute();
request.isAdd = booleanToByte(isAdd);
- request.nextHopWeight = 1; // default value used in make test
- final InSegment inSegment = config.getInSegment();
- checkArgument(inSegment != null, "Configuring impose-and-forward, but in-segment is missing.");
+ translate(config.getInSegment(), request);
+ translate(config.getOutSegment(), request, ctx);
+
+ // default values based on inspecting VPP's CLI and make test code
+ request.nextHopWeight = 1;
+ request.nextHopViaLabel = MPLS_LABEL_INVALID;
+
+ getReplyForWrite(vppApi.ipAddDelRoute(request).toCompletableFuture(), id);
+ }
- checkArgument(inSegment.getType() instanceof IpPrefix, "Only ip-prefix type is supported, but %s given.",
- inSegment.getType());
- final Ipv4Prefix prefix = ((IpPrefix) inSegment.getType()).getIpPrefix().getIpv4Prefix();
+ private void translate(@Nonnull final InSegment inSegment, @Nonnull final IpAddDelRoute request) {
+ checkArgument(inSegment != null, "Configuring impose-and-forward, but in-segment is missing.");
+ final Type type = inSegment.getType();
+ checkArgument(type instanceof IpPrefix, "Only ip-prefix type is supported, but %s given.", type);
// TODO(HC2VPP-264): add support for mpls + v6
+ final Ipv4Prefix prefix = ((IpPrefix) type).getIpPrefix().getIpv4Prefix();
request.dstAddressLength = extractPrefix(prefix);
request.dstAddress = ipv4AddressPrefixToArray(prefix);
+ }
- final OutSegment outSegment = config.getOutSegment();
+ private void translate(@Nonnull final OutSegment outSegment, @Nonnull final IpAddDelRoute request,
+ @Nonnull final MappingContext ctx) {
checkArgument(outSegment != null, "Configuring impose-and-forward, but out-segment is missing.");
- translateOutSegment(outSegment, request, ctx);
- getReplyForWrite(vppApi.ipAddDelRoute(request).toCompletableFuture(), id);
- }
- private void translateOutSegment(@Nonnull final OutSegment outSegment, @Nonnull final IpAddDelRoute request,
- @Nonnull final MappingContext ctx) {
final String outgoingInterface;
if (outSegment instanceof SimplePath) {
- final SimplePath path = (SimplePath) outSegment;
- outgoingInterface = path.getOutgoingInterface();
- final IpAddress nextHop = path.getNextHop();
- checkArgument(nextHop != null, "Configuring impose-and-forward, but next-hop is missing.");
- // TODO(HC2VPP-264): add support for mpls + v6
- checkArgument(nextHop.getIpv4Address() != null, "Only IPv4 next-hop address is supported.");
- request.nextHopAddress = ipv4AddressNoZoneToArray(nextHop.getIpv4Address().getValue());
- request.nextHopNOutLabels = 1;
- checkArgument(path.getOutgoingLabel() != null,
- "Configuring impose-and-forward, but outgoing-label is missing.");
- request.nextHopOutLabelStack = new int[] {path.getOutgoingLabel().getValue().intValue()};
+ outgoingInterface = translate((SimplePath) outSegment, request);
} else if (outSegment instanceof PathList) {
- final PathList pathList = (PathList) outSegment;
- checkArgument(pathList.getPaths() != null && pathList.getPaths().size() == 1,
- "Only single path is supported");
- final Paths paths = pathList.getPaths().get(0);
- outgoingInterface = paths.getOutgoingInterface();
- // TODO(HC2VPP-264): add support for mpls + v6
- final IpAddress nextHop = paths.getNextHop();
- checkArgument(nextHop != null, "Configuring impose-and-forward, but next-hop is missing.");
- checkArgument(nextHop.getIpv4Address() != null, "Only IPv4 next-hop address is supported.");
- request.nextHopAddress = ipv4AddressNoZoneToArray(nextHop.getIpv4Address().getValue());
- final List<MplsLabel> outgoingLabels = paths.getOutgoingLabels();
- final int numberOfLabels = outgoingLabels.size();
- checkArgument(numberOfLabels > 0 && numberOfLabels < MAX_LABELS,
- "Number of labels (%s) not in range (0, %s].", numberOfLabels, MAX_LABELS, numberOfLabels);
- request.nextHopNOutLabels = (byte) numberOfLabels;
- request.nextHopOutLabelStack =
- outgoingLabels.stream().mapToInt(label -> label.getValue().intValue()).toArray();
+ outgoingInterface = translate((PathList) outSegment, request);
} else {
throw new IllegalArgumentException("Unsupported out-segment type: " + outSegment);
}
checkArgument(outgoingInterface != null, "Configuring impose-and-forward, but outgoing-interface is missing.");
request.nextHopSwIfIndex = interfaceContext.getIndex(outgoingInterface, ctx);
- request.nextHopViaLabel = MPLS_LABEL_INVALID;
+ }
+
+ private String translate(@Nonnull final SimplePath path, @Nonnull final IpAddDelRoute request) {
+ final IpAddress nextHop = path.getNextHop();
+ checkArgument(nextHop != null, "Configuring impose-and-forward, but next-hop is missing.");
+
+ // TODO(HC2VPP-264): add support for mpls + v6
+ final Ipv4Address address = nextHop.getIpv4Address();
+ checkArgument(address != null, "Only IPv4 next-hop address is supported.");
+ request.nextHopAddress = ipv4AddressNoZoneToArray(address.getValue());
+
+ final MplsLabel outgoingLabel = path.getOutgoingLabel();
+ checkArgument(outgoingLabel != null, "Configuring impose-and-forward, but outgoing-label is missing.");
+ request.nextHopOutLabelStack = new int[] {outgoingLabel.getValue().intValue()};
+ request.nextHopNOutLabels = 1;
+
+ return path.getOutgoingInterface();
+ }
+
+ private String translate(@Nonnull final PathList pathList, @Nonnull final IpAddDelRoute request) {
+ checkArgument(pathList.getPaths() != null && pathList.getPaths().size() == 1, "Only single path is supported");
+ final Paths paths = pathList.getPaths().get(0);
+ final IpAddress nextHop = paths.getNextHop();
+ checkArgument(nextHop != null, "Configuring impose-and-forward, but next-hop is missing.");
+
+ // TODO(HC2VPP-264): add support for mpls + v6
+ final Ipv4Address address = nextHop.getIpv4Address();
+ checkArgument(address != null, "Only IPv4 next-hop address is supported.");
+ request.nextHopAddress = ipv4AddressNoZoneToArray(address.getValue());
+
+ final List<MplsLabel> labels = paths.getOutgoingLabels();
+ final int numberOfLabels = labels.size();
+ checkArgument(numberOfLabels > 0 && numberOfLabels < MAX_LABELS, "Number of labels (%s) not in range (0, %s].",
+ numberOfLabels, MAX_LABELS, numberOfLabels);
+ request.nextHopNOutLabels = (byte) numberOfLabels;
+ request.nextHopOutLabelStack = labels.stream().mapToInt(label -> label.getValue().intValue()).toArray();
+
+ return paths.getOutgoingInterface();
}
}
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/Ipv4LookupWriter.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/Ipv4LookupWriter.java
index 7ac164a96..94862c52b 100644
--- a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/Ipv4LookupWriter.java
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/Ipv4LookupWriter.java
@@ -25,8 +25,6 @@ import io.fd.vpp.jvpp.core.future.FutureJVppCore;
import javax.annotation.Nonnull;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp.Config;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.InSegment;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.type.MplsLabel;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLsp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.mpls.rev171120.LookupType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.mpls.rev171120.StaticLspVppLookupAugmentation;
@@ -38,7 +36,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
*
* @see <a href="https://git.fd.io/vpp/tree/src/vnet/mpls/mpls.api">mpls_route_add_del</a> definition
*/
-final class Ipv4LookupWriter implements LspWriter {
+final class Ipv4LookupWriter implements LspWriter, MplsInSegmentTranslator {
private static final byte IPV4_PROTOCOL = (byte) LookupType.Ipv4.getIntValue();
private final FutureJVppCore vppApi;
@@ -55,8 +53,8 @@ final class Ipv4LookupWriter implements LspWriter {
request.mrIsAdd = booleanToByte(isAdd);
- translate(request, config);
- translate(request, config.getAugmentation(StaticLspVppLookupAugmentation.class));
+ translate(config.getInSegment(), request);
+ translate(config.getAugmentation(StaticLspVppLookupAugmentation.class), request);
// default values based on inspecting VPP's CLI and make test code
request.mrClassifyTableIndex = -1;
@@ -70,23 +68,12 @@ final class Ipv4LookupWriter implements LspWriter {
getReplyForWrite(vppApi.mplsRouteAddDel(request).toCompletableFuture(), id);
}
- private void translate(@Nonnull final MplsRouteAddDel request, @Nonnull final Config config) {
- final InSegment inSegment = config.getInSegment();
- checkArgument(inSegment != null, "Configuring ipv4 pop-and-lookup, but in-segment is missing.");
-
- checkArgument(inSegment.getType() instanceof MplsLabel, "Expecting mpls-label in-segment type, but %s given.",
- inSegment.getType());
- final Long label = ((MplsLabel) inSegment.getType()).getIncomingLabel().getValue();
- request.mrLabel = label.intValue();
- }
-
- private void translate(@Nonnull final MplsRouteAddDel request,
- @Nonnull final StaticLspVppLookupAugmentation vppLabelLookup) {
+ private void translate(@Nonnull final StaticLspVppLookupAugmentation vppLabelLookup,
+ @Nonnull final MplsRouteAddDel request) {
// IPv4 lookup should only happen if there is no more labels to process, so setting the EOS bit:
request.mrEos = 1;
- final Long lookupInTable = vppLabelLookup.getLabelLookup().getIp4LookupInTable();
- checkArgument(lookupInTable != null,
- "Configuring pop and ipv4 lookup, but ipv4 lookup table was not given");
- request.mrNextHopTableId = lookupInTable.intValue();
+ final Long lookupTable = vppLabelLookup.getLabelLookup().getIp4LookupInTable();
+ checkArgument(lookupTable != null, "Configuring pop and ipv4 lookup, but ipv4 lookup table was not given");
+ request.mrNextHopTableId = lookupTable.intValue();
}
}
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsInSegmentTranslator.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsInSegmentTranslator.java
new file mode 100644
index 000000000..9f531bba4
--- /dev/null
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsInSegmentTranslator.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.mpls;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.vpp.jvpp.core.dto.MplsRouteAddDel;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.InSegment;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.Type;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.type.MplsLabel;
+
+/**
+ * Mixin that translates {@link InSegment} of {@link MplsLabel} type to {@link MplsRouteAddDel} message.
+ */
+interface MplsInSegmentTranslator {
+ default void translate(@Nonnull final InSegment inSegment, @Nonnull final MplsRouteAddDel request) {
+ checkArgument(inSegment != null, "Missing in-segment");
+ final Type type = inSegment.getType();
+ checkArgument(type instanceof MplsLabel, "Expecting in-segment of type mpls-label, but %s given.", type);
+ final Long label = ((MplsLabel) type).getIncomingLabel().getValue();
+ request.mrLabel = label.intValue();
+ }
+}
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsLookupWriter.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsLookupWriter.java
index c845c7aeb..d5d4872dc 100644
--- a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsLookupWriter.java
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsLookupWriter.java
@@ -25,8 +25,6 @@ import io.fd.vpp.jvpp.core.future.FutureJVppCore;
import javax.annotation.Nonnull;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp.Config;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.InSegment;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.type.MplsLabel;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLsp;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.mpls.rev171120.LookupType;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.mpls.rev171120.StaticLspVppLookupAugmentation;
@@ -38,7 +36,7 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
*
* @see <a href="https://git.fd.io/vpp/tree/src/vnet/mpls/mpls.api">mpls_route_add_del</a> definition
*/
-final class MplsLookupWriter implements LspWriter {
+final class MplsLookupWriter implements LspWriter, MplsInSegmentTranslator {
private static final byte MPLS_PROTOCOL = (byte) LookupType.Mpls.getIntValue();
private final FutureJVppCore vppApi;
@@ -55,8 +53,8 @@ final class MplsLookupWriter implements LspWriter {
request.mrIsAdd = booleanToByte(isAdd);
- translate(request, config);
- translate(request, config.getAugmentation(StaticLspVppLookupAugmentation.class));
+ translate(config.getInSegment(), request);
+ translate(config.getAugmentation(StaticLspVppLookupAugmentation.class), request);
// default values based on inspecting VPP's CLI and make test code
request.mrClassifyTableIndex = -1;
@@ -70,24 +68,13 @@ final class MplsLookupWriter implements LspWriter {
getReplyForWrite(vppApi.mplsRouteAddDel(request).toCompletableFuture(), id);
}
- private void translate(@Nonnull final MplsRouteAddDel request, @Nonnull final Config config) {
- final InSegment inSegment = config.getInSegment();
- checkArgument(inSegment != null, "Configuring mpls pop-and-lookup, but in-segment is missing.");
-
- checkArgument(inSegment.getType() instanceof MplsLabel, "Expecting mpls-label in-segment type, but %s given.",
- inSegment.getType());
- final Long label = ((MplsLabel) inSegment.getType()).getIncomingLabel().getValue();
- request.mrLabel = label.intValue();
- }
-
- private void translate(@Nonnull final MplsRouteAddDel request,
- @Nonnull final StaticLspVppLookupAugmentation vppLabelLookup) {
+ private void translate(@Nonnull final StaticLspVppLookupAugmentation vppLabelLookup,
+ @Nonnull final MplsRouteAddDel request) {
// MPLS lookup for the last label is not valid operation (there is no next label to lookup),
// so match only labels without EOS bit set:
request.mrEos = 0;
- final Long mplsLookupInTable = vppLabelLookup.getLabelLookup().getMplsLookupInTable();
- checkArgument(mplsLookupInTable != null,
- "Configuring pop and mpls lookup, but MPLS lookup table was not given");
- request.mrNextHopTableId = mplsLookupInTable.intValue();
+ final Long lookupTable = vppLabelLookup.getLabelLookup().getMplsLookupInTable();
+ checkArgument(lookupTable != null, "Configuring pop and mpls lookup, but MPLS lookup table was not given");
+ request.mrNextHopTableId = lookupTable.intValue();
}
}
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsSwapWriter.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsSwapWriter.java
new file mode 100644
index 000000000..8d834fe7c
--- /dev/null
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/MplsSwapWriter.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.mpls;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import io.fd.hc2vpp.common.translate.util.Ipv4Translator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.MplsRouteAddDel;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp.Config;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.OutSegment;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.out.segment.SimplePath;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLsp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.routing.types.rev170227.MplsLabel;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+/**
+ * Translates {@link StaticLspConfig.Operation#SwapAndForward} operation to mpls_route_add_del API.
+ *
+ * @see <a href="https://git.fd.io/vpp/tree/src/vnet/mpls/mpls.api">mpls_route_add_del</a> definition
+ */
+final class MplsSwapWriter implements LspWriter, Ipv4Translator, MplsInSegmentTranslator {
+
+ private final FutureJVppCore vppApi;
+ private final NamingContext interfaceContext;
+
+ MplsSwapWriter(@Nonnull final FutureJVppCore vppApi, @Nonnull final NamingContext interfaceContext) {
+ this.vppApi = vppApi;
+ this.interfaceContext = interfaceContext;
+ }
+
+ @Override
+ public void write(@Nonnull final InstanceIdentifier<StaticLsp> id, @Nonnull final StaticLsp data,
+ @Nonnull final MappingContext ctx, final boolean isAdd) throws WriteFailedException {
+ final Config config = data.getConfig();
+ final MplsRouteAddDel request = new MplsRouteAddDel();
+ request.mrIsAdd = booleanToByte(isAdd);
+ request.mrEos = 1; // only SWAP for the last label in the stack is currently supported
+
+ translate(config.getInSegment(), request);
+ translate(config.getOutSegment(), request, ctx);
+
+ // default values based on inspecting VPP's CLI and make test code
+ request.mrClassifyTableIndex = -1;
+ request.mrNextHopWeight = 1;
+ request.mrNextHopViaLabel = MPLS_LABEL_INVALID;
+
+ getReplyForWrite(vppApi.mplsRouteAddDel(request).toCompletableFuture(), id);
+ }
+
+ private void translate(@Nonnull final OutSegment outSegment, @Nonnull final MplsRouteAddDel request,
+ @Nonnull final MappingContext ctx) {
+ checkArgument(outSegment instanceof SimplePath, "Unsupported out-segment type: %s", outSegment);
+ final SimplePath path = (SimplePath) outSegment;
+ final IpAddress nextHop = path.getNextHop();
+ checkArgument(nextHop != null, "Configuring swap-and-forward, but next-hop is missing.");
+
+ // TODO(HC2VPP-264): add support for mpls + v6
+ final Ipv4Address address = nextHop.getIpv4Address();
+ checkArgument(address != null, "Only IPv4 next-hop address is supported.");
+ request.mrNextHop = ipv4AddressNoZoneToArray(address.getValue());
+
+ final MplsLabel outgoingLabel = path.getOutgoingLabel();
+ checkArgument(outgoingLabel != null, "Configuring swap-and-forward, but outgoing-label is missing.");
+ request.mrNextHopOutLabelStack = new int[] {outgoingLabel.getValue().intValue()};
+ request.mrNextHopNOutLabels = 1;
+
+ final String outgoingInterface = path.getOutgoingInterface();
+ checkArgument(outgoingInterface != null, "Configuring swap-and-forward, but outgoing-interface is missing.");
+ request.mrNextHopSwIfIndex = interfaceContext.getIndex(outgoingInterface, ctx);
+ }
+}
diff --git a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/StaticLspCustomizer.java b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/StaticLspCustomizer.java
index 54653b9c1..d93e0343c 100644
--- a/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/StaticLspCustomizer.java
+++ b/mpls/impl/src/main/java/io/fd/hc2vpp/mpls/StaticLspCustomizer.java
@@ -20,6 +20,7 @@ import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig.Operation.ImposeAndForward;
import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig.Operation.PopAndLookup;
+import static org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig.Operation.SwapAndForward;
import io.fd.hc2vpp.common.translate.util.NamingContext;
import io.fd.honeycomb.translate.MappingContext;
@@ -47,6 +48,7 @@ final class StaticLspCustomizer implements ListWriterCustomizer<StaticLsp, Stati
private final ImposeAndForwardWriter imposeAndForward;
private final MplsLookupWriter mplsLookup;
private final Ipv4LookupWriter ipv4Lookup;
+ private final MplsSwapWriter mplsSwap;
StaticLspCustomizer(@Nonnull final FutureJVppCore vppApi, @Nonnull NamingContext interfaceContext) {
checkNotNull(vppApi, "vppApi should not be null");
@@ -54,6 +56,7 @@ final class StaticLspCustomizer implements ListWriterCustomizer<StaticLsp, Stati
this.imposeAndForward = new ImposeAndForwardWriter(vppApi, interfaceContext);
this.mplsLookup = new MplsLookupWriter(vppApi);
this.ipv4Lookup = new Ipv4LookupWriter(vppApi);
+ this.mplsSwap = new MplsSwapWriter(vppApi, interfaceContext);
}
@Override
@@ -94,6 +97,8 @@ final class StaticLspCustomizer implements ListWriterCustomizer<StaticLsp, Stati
} else {
throw new IllegalArgumentException("Unsupported lookup type: " + type);
}
+ } else if (SwapAndForward.equals(operation)) {
+ mplsSwap.write(id, data, ctx, isAdd);
} else {
throw new IllegalArgumentException("Unsupported operation: " + operation);
}
diff --git a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/ImposeAndForwardTest.java b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/ImposeAndForwardTest.java
index 59db8426f..50c86ac26 100644
--- a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/ImposeAndForwardTest.java
+++ b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/ImposeAndForwardTest.java
@@ -56,11 +56,9 @@ public class ImposeAndForwardTest extends WriterCustomizerTest implements ByteDa
private static final String IF_NAME = "local0";
private static final int IF_INDEX = 123;
private static final String LSP_NAME = "static-lsp0";
-
private static final InstanceIdentifier<StaticLsp> IID = InstanceIdentifier.create(Routing.class).augmentation
(Routing1.class).child(Mpls.class).augmentation(Mpls1.class).child(StaticLsps.class)
.child(StaticLsp.class, new StaticLspKey(LSP_NAME));
-
private static final StaticLsp SIMPLE_LSP = getSimpleLsp();
private static final StaticLsp COMPLEX_LSP = getComplexLsp();
@@ -69,7 +67,7 @@ public class ImposeAndForwardTest extends WriterCustomizerTest implements ByteDa
private StaticLspCustomizer customizer;
private static StaticLsp getSimpleLsp() {
- final StaticLsp data = new StaticLspBuilder()
+ return new StaticLspBuilder()
.setName(LSP_NAME)
.setConfig(new ConfigBuilder()
.setInSegment(new InSegmentBuilder()
@@ -85,11 +83,10 @@ public class ImposeAndForwardTest extends WriterCustomizerTest implements ByteDa
.build())
.build())
.build();
- return data;
}
private static StaticLsp getComplexLsp() {
- final StaticLsp data = new StaticLspBuilder()
+ return new StaticLspBuilder()
.setName(LSP_NAME)
.setConfig(new ConfigBuilder()
.setInSegment(new InSegmentBuilder()
@@ -107,7 +104,6 @@ public class ImposeAndForwardTest extends WriterCustomizerTest implements ByteDa
.build())
.build())
.build();
- return data;
}
@Override
diff --git a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/MplsInterfaceCustomizerTest.java b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/MplsInterfaceCustomizerTest.java
index 8fa1abf29..21551292b 100644
--- a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/MplsInterfaceCustomizerTest.java
+++ b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/MplsInterfaceCustomizerTest.java
@@ -44,10 +44,8 @@ public class MplsInterfaceCustomizerTest extends WriterCustomizerTest implements
private static final String IF_NAME = "local0";
private static final int IF_INDEX = 123;
-
private static final Interface MPLS_ENABLED = getInterfaceMpls(true);
private static final Interface MPLS_DISABLED = getInterfaceMpls(false);
-
private static final InstanceIdentifier<Mpls> MPLS_ID = InstanceIdentifier.create(Routing.class).augmentation
(Routing1.class).child(Mpls.class);
private static final InstanceIdentifier<Interface> IID = MPLS_ID.child(Interface.class, new InterfaceKey(IF_NAME));
@@ -57,13 +55,12 @@ public class MplsInterfaceCustomizerTest extends WriterCustomizerTest implements
private MplsInterfaceCustomizer customizer;
private static Interface getInterfaceMpls(final boolean enabled) {
- final Interface data = new InterfaceBuilder()
+ return new InterfaceBuilder()
.setName(IF_NAME)
.setConfig(new ConfigBuilder()
.setEnabled(enabled)
.build())
.build();
- return data;
}
@Override
diff --git a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndIpv4LookupTest.java b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndIpv4LookupTest.java
index b76daaa04..e924cfaaf 100644
--- a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndIpv4LookupTest.java
+++ b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndIpv4LookupTest.java
@@ -53,14 +53,12 @@ public class PopAndIpv4LookupTest extends WriterCustomizerTest implements ByteDa
private static final String IF_NAME = "local0";
private static final int IF_INDEX = 123;
private static final String LSP_NAME = "static-lsp0";
-
private static final InstanceIdentifier<StaticLsp> IID = InstanceIdentifier.create(Routing.class).augmentation
(Routing1.class).child(Mpls.class).augmentation(Mpls1.class).child(StaticLsps.class)
.child(StaticLsp.class, new StaticLspKey(LSP_NAME));
-
- private static final StaticLsp POP_AND_IP4_LOOKUP = getStaticLsp();
private static final int IP4_TABLE_ID = 456;
private static final int LOCAL_LABEL = 104;
+ private static final StaticLsp POP_AND_IP4_LOOKUP = getStaticLsp();
@Mock
private FutureJVppCoreFacade jvpp;
@@ -70,7 +68,7 @@ public class PopAndIpv4LookupTest extends WriterCustomizerTest implements ByteDa
* Equivalent of mpls local-label add eos 104 ip4-lookup-in-table 456
*/
private static StaticLsp getStaticLsp() {
- final StaticLsp data = new StaticLspBuilder()
+ return new StaticLspBuilder()
.setName(LSP_NAME)
.setConfig(new ConfigBuilder()
.setInSegment(new InSegmentBuilder()
@@ -87,7 +85,6 @@ public class PopAndIpv4LookupTest extends WriterCustomizerTest implements ByteDa
.build())
.build())
.build();
- return data;
}
@Override
@@ -101,16 +98,16 @@ public class PopAndIpv4LookupTest extends WriterCustomizerTest implements ByteDa
@Test
public void testWrite() throws WriteFailedException {
customizer.writeCurrentAttributes(IID, POP_AND_IP4_LOOKUP, writeContext);
- verify(jvpp).mplsRouteAddDel(getRequestForSimpleLsp(true));
+ verify(jvpp).mplsRouteAddDel(getRequest(true));
}
@Test
public void testDelete() throws WriteFailedException {
customizer.deleteCurrentAttributes(IID, POP_AND_IP4_LOOKUP, writeContext);
- verify(jvpp).mplsRouteAddDel(getRequestForSimpleLsp(false));
+ verify(jvpp).mplsRouteAddDel(getRequest(false));
}
- private MplsRouteAddDel getRequestForSimpleLsp(final boolean add) {
+ private MplsRouteAddDel getRequest(final boolean add) {
MplsRouteAddDel request = new MplsRouteAddDel();
request.mrLabel = LOCAL_LABEL;
request.mrEos = 1;
diff --git a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndMplsLookupTest.java b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndMplsLookupTest.java
index ef971093d..de32a1907 100644
--- a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndMplsLookupTest.java
+++ b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/PopAndMplsLookupTest.java
@@ -53,14 +53,12 @@ public class PopAndMplsLookupTest extends WriterCustomizerTest implements ByteDa
private static final String IF_NAME = "local0";
private static final int IF_INDEX = 123;
private static final String LSP_NAME = "static-lsp0";
-
private static final InstanceIdentifier<StaticLsp> IID = InstanceIdentifier.create(Routing.class).augmentation
(Routing1.class).child(Mpls.class).augmentation(Mpls1.class).child(StaticLsps.class)
.child(StaticLsp.class, new StaticLspKey(LSP_NAME));
-
- private static final StaticLsp POP_AND_MPLS_LOOKUP = getStaticLsp();
private static final int MPLS_TABLE_ID = 456;
private static final int LOCAL_LABEL = 102;
+ private static final StaticLsp POP_AND_MPLS_LOOKUP = getStaticLsp();
@Mock
private FutureJVppCoreFacade jvpp;
@@ -70,7 +68,7 @@ public class PopAndMplsLookupTest extends WriterCustomizerTest implements ByteDa
* Equivalent of mpls local-label add non-eos 102 mpls-lookup-in-table 456
*/
private static StaticLsp getStaticLsp() {
- final StaticLsp data = new StaticLspBuilder()
+ return new StaticLspBuilder()
.setName(LSP_NAME)
.setConfig(new ConfigBuilder()
.setInSegment(new InSegmentBuilder()
@@ -87,7 +85,6 @@ public class PopAndMplsLookupTest extends WriterCustomizerTest implements ByteDa
.build())
.build())
.build();
- return data;
}
@Override
@@ -101,16 +98,16 @@ public class PopAndMplsLookupTest extends WriterCustomizerTest implements ByteDa
@Test
public void testWrite() throws WriteFailedException {
customizer.writeCurrentAttributes(IID, POP_AND_MPLS_LOOKUP, writeContext);
- verify(jvpp).mplsRouteAddDel(getRequestForSimpleLsp(true));
+ verify(jvpp).mplsRouteAddDel(getRequest(true));
}
@Test
public void testDelete() throws WriteFailedException {
customizer.deleteCurrentAttributes(IID, POP_AND_MPLS_LOOKUP, writeContext);
- verify(jvpp).mplsRouteAddDel(getRequestForSimpleLsp(false));
+ verify(jvpp).mplsRouteAddDel(getRequest(false));
}
- private MplsRouteAddDel getRequestForSimpleLsp(final boolean add) {
+ private MplsRouteAddDel getRequest(final boolean add) {
MplsRouteAddDel request = new MplsRouteAddDel();
request.mrLabel = LOCAL_LABEL;
request.mrEos = 0;
diff --git a/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/SwapAndForwardTest.java b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/SwapAndForwardTest.java
new file mode 100644
index 000000000..6fd9e02f5
--- /dev/null
+++ b/mpls/impl/src/test/java/io/fd/hc2vpp/mpls/SwapAndForwardTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.mpls;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import io.fd.hc2vpp.common.test.write.WriterCustomizerTest;
+import io.fd.hc2vpp.common.translate.util.ByteDataTranslator;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.MplsRouteAddDel;
+import io.fd.vpp.jvpp.core.dto.MplsRouteAddDelReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddressBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.Mpls1;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.StaticLspConfig;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp.ConfigBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.InSegmentBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.in.segment.type.MplsLabelBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310._static.lsp_config.out.segment.SimplePathBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls.StaticLsps;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLsp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLspBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls._static.rev170310.routing.mpls._static.lsps.StaticLspKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls.rev170702.Routing1;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.mpls.rev170702.routing.Mpls;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.routing.rev140524.Routing;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.routing.types.rev170227.MplsLabel;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public class SwapAndForwardTest extends WriterCustomizerTest implements ByteDataTranslator {
+
+ private static final String IF_NAME = "local0";
+ private static final int IF_INDEX = 123;
+ private static final String LSP_NAME = "static-lsp0";
+ private static final InstanceIdentifier<StaticLsp> IID = InstanceIdentifier.create(Routing.class).augmentation
+ (Routing1.class).child(Mpls.class).augmentation(Mpls1.class).child(StaticLsps.class)
+ .child(StaticLsp.class, new StaticLspKey(LSP_NAME));
+ private static final int LOCAL_LABEL = 104;
+ private static final int OUT_LABEL = 104;
+ private static final StaticLsp SWAP_AND_FORWARD = getStaticLsp();
+
+ @Mock
+ private FutureJVppCoreFacade jvpp;
+ private StaticLspCustomizer customizer;
+
+ /**
+ * Equivalent of
+ *
+ * mpls local-label add eos 104 via 10.10.24.4 local0 out-labels 104
+ */
+ private static StaticLsp getStaticLsp() {
+ return new StaticLspBuilder()
+ .setName(LSP_NAME)
+ .setConfig(new ConfigBuilder()
+ .setInSegment(new InSegmentBuilder()
+ .setType(new MplsLabelBuilder().setIncomingLabel(new MplsLabel((long) LOCAL_LABEL))
+ .build())
+ .build()
+ )
+ .setOperation(StaticLspConfig.Operation.SwapAndForward)
+ .setOutSegment(new SimplePathBuilder()
+ .setNextHop(IpAddressBuilder.getDefaultInstance("10.10.24.4"))
+ .setOutgoingInterface(IF_NAME)
+ .setOutgoingLabel(new MplsLabel((long) OUT_LABEL))
+ .build())
+ .build())
+ .build();
+ }
+
+ @Override
+ public void setUpTest() {
+ final String ctxInstanceName = "test-ifc-context";
+ customizer = new StaticLspCustomizer(jvpp, new NamingContext("test-prefix", ctxInstanceName));
+ when(jvpp.mplsRouteAddDel(any())).thenReturn(future(new MplsRouteAddDelReply()));
+ defineMapping(mappingContext, IF_NAME, IF_INDEX, ctxInstanceName);
+ }
+
+ @Test
+ public void testWrite() throws WriteFailedException {
+ customizer.writeCurrentAttributes(IID, SWAP_AND_FORWARD, writeContext);
+ verify(jvpp).mplsRouteAddDel(getRequest(true));
+ }
+
+ @Test
+ public void testDelete() throws WriteFailedException {
+ customizer.deleteCurrentAttributes(IID, SWAP_AND_FORWARD, writeContext);
+ verify(jvpp).mplsRouteAddDel(getRequest(false));
+ }
+
+ private MplsRouteAddDel getRequest(final boolean add) {
+ final MplsRouteAddDel request = new MplsRouteAddDel();
+ request.mrLabel = LOCAL_LABEL;
+ request.mrEos = 1;
+ request.mrClassifyTableIndex = -1; // default value used in make test
+ request.mrIsAdd = booleanToByte(add);
+ request.mrNextHopWeight = 1; // default value used in make test
+ request.mrNextHop = new byte[] {10, 10, 24, 4};
+ request.mrNextHopSwIfIndex = IF_INDEX;
+ request.mrNextHopViaLabel = LspWriter.MPLS_LABEL_INVALID; // default value used by make test
+ request.mrNextHopNOutLabels = 1;
+ request.mrNextHopOutLabelStack = new int[] {OUT_LABEL};
+ return request;
+ }
+}