From 29e848b9868eedc2bc241247128cb2d8ed0aa006 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Mon, 5 Sep 2016 13:31:39 +0200 Subject: HONEYCOMB-153: fix acls for sub-interfaces Change-Id: Ia75c2ebd62fb371fd60f860fe12761926f443b98 Signed-off-by: Marek Gradzki --- .../v3po/interfaces/SubInterfaceAclCustomizer.java | 9 +-- .../v3po/interfaces/SubInterfaceCustomizer.java | 21 ++----- .../v3po/interfaces/acl/AbstractAceWriter.java | 27 +++++++-- .../v3po/interfaces/acl/AceEthWriter.java | 6 +- .../v3po/interfaces/acl/AceIp4Writer.java | 32 ++++++---- .../v3po/interfaces/acl/AceIp6Writer.java | 44 ++++++++------ .../translate/v3po/interfaces/acl/AceWriter.java | 4 +- .../v3po/interfaces/acl/IetfAClWriter.java | 9 ++- .../translate/v3po/interfaces/acl/Readme.adoc | 5 +- .../acl/SubInterfaceIetfAclCustomizer.java | 11 +++- .../translate/v3po/util/SubInterfaceUtils.java | 52 ++++++++++++++++ .../v3po/interfaces/acl/AceEthWriterTest.java | 4 +- .../v3po/interfaces/acl/AceIp4WriterTest.java | 70 ++++++++++++++++++---- .../v3po/interfaces/acl/AceIp6WriterTest.java | 68 +++++++++++++++++---- .../v3po/interfaces/acl/AceIpWriterTestUtils.java | 34 +++++++++++ .../translate/v3po/util/SubinterfaceUtilsTest.java | 32 ++++++++++ 16 files changed, 338 insertions(+), 90 deletions(-) create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/util/SubInterfaceUtils.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIpWriterTestUtils.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/util/SubinterfaceUtilsTest.java (limited to 'v3po') diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceAclCustomizer.java index d4a39980b..962801a38 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceAclCustomizer.java @@ -18,13 +18,13 @@ package io.fd.honeycomb.translate.v3po.interfaces; import static com.google.common.base.Preconditions.checkNotNull; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; import io.fd.honeycomb.translate.v3po.util.FutureJVppCustomizer; +import io.fd.honeycomb.translate.v3po.util.NamingContext; +import io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils; import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.honeycomb.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.translate.v3po.util.NamingContext; -import io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils; import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; @@ -47,7 +47,8 @@ public class SubInterfaceAclCustomizer extends FutureJVppCustomizer private final NamingContext interfaceContext; private final NamingContext classifyTableContext; - public SubInterfaceAclCustomizer(@Nonnull final FutureJVppCore vppApi, @Nonnull final NamingContext interfaceContext, + public SubInterfaceAclCustomizer(@Nonnull final FutureJVppCore vppApi, + @Nonnull final NamingContext interfaceContext, @Nonnull final NamingContext classifyTableContext) { super(vppApi); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceCustomizer.java index 38164bfbb..bfaa42e44 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/SubInterfaceCustomizer.java @@ -17,18 +17,18 @@ package io.fd.honeycomb.translate.v3po.interfaces; import static com.google.common.base.Preconditions.checkState; +import static io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils.getNumberOfTags; import static io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils.getSubInterfaceName; import static io.fd.honeycomb.translate.v3po.util.TranslateUtils.booleanToByte; import com.google.common.base.Preconditions; import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; import io.fd.honeycomb.translate.v3po.util.FutureJVppCustomizer; -import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; -import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.v3po.util.TranslateUtils; +import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; +import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; -import java.util.List; import java.util.Objects; import java.util.concurrent.CompletionStage; import javax.annotation.Nonnull; @@ -44,7 +44,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.match.attributes.MatchType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.match.attributes.match.type.Default; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.match.attributes.match.type.vlan.tagged.VlanTagged; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Tags; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.tags.Tag; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.openvpp.jvpp.VppBaseCallException; @@ -108,7 +107,7 @@ public class SubInterfaceCustomizer extends FutureJVppCustomizer request.subId = Math.toIntExact(subInterface.getIdentifier().intValue()); request.swIfIndex = swIfIndex; - final int numberOfTags = getNumberOfTags(subInterface); + final int numberOfTags = getNumberOfTags(subInterface.getTags()); switch (numberOfTags) { case 0: request.noTags = 1; @@ -155,18 +154,6 @@ public class SubInterfaceCustomizer extends FutureJVppCustomizer request.innerVlanIdAny = booleanToByte(Dot1qTag.VlanId.Enumeration.Any.equals(vlanId.getEnumeration())); } - private static int getNumberOfTags(@Nonnull final SubInterface subInterface) { - final Tags tags = subInterface.getTags(); - if (tags == null) { - return 0; - } - final List tagList = tags.getTag(); - if (tagList == null) { - return 0; - } - return tagList.size(); - } - private static short dot1qVlanIdToShort(@Nullable Dot1qVlanId dot1qVlanId) { if (dot1qVlanId == null) { return 0; // tell VPP that optional argument is missing diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java index 21a710701..eeabff4ce 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java @@ -16,14 +16,17 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl; +import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import com.google.common.annotations.VisibleForTesting; import io.fd.honeycomb.translate.util.RWUtils; import io.fd.honeycomb.translate.v3po.util.TranslateUtils; import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; import java.util.List; import java.util.concurrent.CompletionStage; import java.util.stream.Collector; +import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; @@ -53,6 +56,9 @@ abstract class AbstractAceWriter implements AceWriter { // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, so setting 8K for now protected static final int TABLE_MEM_SIZE = 8 * 1024; + @VisibleForTesting + static final int VLAN_TAG_LEN = 4; + private static final Collector SINGLE_ITEM_COLLECTOR = RWUtils.singleItemCollector(); @@ -68,11 +74,13 @@ abstract class AbstractAceWriter implements AceWriter { * @param action packet handling action (permit/deny) * @param ace ACE to be translated * @param nextTableIndex classify table index + * @param vlanTags number of vlan tags * @return classify table that represents given ACE */ protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, @Nonnull final T ace, - final int nextTableIndex); + final int nextTableIndex, + final int vlanTags); /** * Creates classify session for given ACE. @@ -80,11 +88,13 @@ abstract class AbstractAceWriter implements AceWriter { * @param action packet handling action (permit/deny) * @param ace ACE to be translated * @param tableIndex classify table index for the given session + * @param vlanTags number of vlan tags * @return classify session that represents given ACE */ protected abstract ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, @Nonnull final T ace, - final int tableIndex); + final int tableIndex, + final int vlanTags); /** * Sets classify table index for input_acl_set_interface request. @@ -94,21 +104,24 @@ abstract class AbstractAceWriter implements AceWriter { */ protected abstract void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex); + @Override public final void write(@Nonnull final InstanceIdentifier id, @Nonnull final List aces, - @Nonnull final InputAclSetInterface request) + @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags) throws VppBaseCallException, WriteTimeoutException { final PacketHandling action = aces.stream().map(ace -> ace.getActions().getPacketHandling()).distinct() .collect(SINGLE_ITEM_COLLECTOR); + checkArgument(vlanTags >= 0 && vlanTags <= 2, "Number of vlan tags %s is not in [0,2] range"); + int nextTableIndex = -1; for (final Ace ace : aces) { // Create table + session per entry final ClassifyAddDelTable ctRequest = - createClassifyTable(action, (T) ace.getMatches().getAceType(), nextTableIndex); + createClassifyTable(action, (T) ace.getMatches().getAceType(), nextTableIndex, vlanTags); nextTableIndex = createClassifyTable(id, ctRequest); createClassifySession(id, - createClassifySession(action, (T) ace.getMatches().getAceType(), nextTableIndex)); + createClassifySession(action, (T) ace.getMatches().getAceType(), nextTableIndex, vlanTags)); } setClassifyTable(request, nextTableIndex); } @@ -160,4 +173,8 @@ abstract class AbstractAceWriter implements AceWriter { return request; } + + protected int getVlanTagsLen(final int vlanTags) { + return vlanTags * VLAN_TAG_LEN; + } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java index 1240a2986..d204e1631 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java @@ -43,7 +43,8 @@ final class AceEthWriter extends AbstractAceWriter { @Override public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, @Nonnull final AceEth aceEth, - @Nonnull final int nextTableIndex) { + @Nonnull final int nextTableIndex, + final int vlanTags) { final ClassifyAddDelTable request = createClassifyTable(action, nextTableIndex); request.mask = new byte[16]; @@ -101,7 +102,8 @@ final class AceEthWriter extends AbstractAceWriter { @Override public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, @Nonnull final AceEth aceEth, - @Nonnull final int tableIndex) { + @Nonnull final int tableIndex, + final int vlanTags) { final ClassifyAddDelSession request = createClassifySession(action, tableIndex); request.match = new byte[16]; diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java index b2a9613fa..857f00393 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4Writer.java @@ -77,7 +77,8 @@ final class AceIp4Writer extends AbstractAceWriter { @Override public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, @Nonnull final AceIp aceIp, - final int nextTableIndex) { + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); @@ -88,14 +89,16 @@ final class AceIp4Writer extends AbstractAceWriter { boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; + final int baseOffset = getVlanTagsLen(vlanTags); + // First 14 bytes represent l2 header (2x6 + etherType(2)) if (aceIp.getProtocol() != null) { // Internet Protocol number - request.mask[IP_VERSION_OFFSET] = (byte) IP_VERSION_MASK; // first 4 bits + request.mask[baseOffset + IP_VERSION_OFFSET] = (byte) IP_VERSION_MASK; // first 4 bits } if (aceIp.getDscp() != null) { aceIsEmpty = false; - request.mask[DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits + request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits } if (aceIp.getSourcePortRange() != null) { @@ -108,13 +111,15 @@ final class AceIp4Writer extends AbstractAceWriter { if (ipVersion.getSourceIpv4Network() != null) { aceIsEmpty = false; - System.arraycopy(toByteMask(ipVersion.getSourceIpv4Network()), 0, request.mask, SRC_IP_OFFSET, IP4_LEN); + System.arraycopy(toByteMask(ipVersion.getSourceIpv4Network()), 0, request.mask, baseOffset + SRC_IP_OFFSET, + IP4_LEN); } if (ipVersion.getDestinationIpv4Network() != null) { aceIsEmpty = false; System - .arraycopy(toByteMask(ipVersion.getDestinationIpv4Network()), 0, request.mask, DST_IP_OFFSET, IP4_LEN); + .arraycopy(toByteMask(ipVersion.getDestinationIpv4Network()), 0, request.mask, + baseOffset + DST_IP_OFFSET, IP4_LEN); } if (aceIsEmpty) { @@ -132,7 +137,8 @@ final class AceIp4Writer extends AbstractAceWriter { @Override public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, @Nonnull final AceIp aceIp, - final int tableIndex) { + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); @@ -141,13 +147,16 @@ final class AceIp4Writer extends AbstractAceWriter { request.match = new byte[TABLE_MASK_LENGTH]; boolean noMatch = true; + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIp.getProtocol() != null) { - request.match[IP_VERSION_OFFSET] = (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4)); + request.match[baseOffset + IP_VERSION_OFFSET] = + (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4)); } if (aceIp.getDscp() != null) { noMatch = false; - request.match[DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2)); + request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2)); } if (aceIp.getSourcePortRange() != null) { @@ -160,12 +169,15 @@ final class AceIp4Writer extends AbstractAceWriter { if (ipVersion.getSourceIpv4Network() != null) { noMatch = false; - System.arraycopy(toMatchValue(ipVersion.getSourceIpv4Network()), 0, request.match, SRC_IP_OFFSET, IP4_LEN); + System + .arraycopy(toMatchValue(ipVersion.getSourceIpv4Network()), 0, request.match, baseOffset + SRC_IP_OFFSET, + IP4_LEN); } if (ipVersion.getDestinationIpv4Network() != null) { noMatch = false; - System.arraycopy(toMatchValue(ipVersion.getDestinationIpv4Network()), 0, request.match, DST_IP_OFFSET, + System.arraycopy(toMatchValue(ipVersion.getDestinationIpv4Network()), 0, request.match, + baseOffset + DST_IP_OFFSET, IP4_LEN); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6Writer.java index c46f2e19d..d847ed845 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6Writer.java @@ -92,7 +92,8 @@ final class AceIp6Writer extends AbstractAceWriter { @Override public ClassifyAddDelTable createClassifyTable(@Nonnull final PacketHandling action, @Nonnull final AceIp aceIp, - final int nextTableIndex) { + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); @@ -102,15 +103,18 @@ final class AceIp6Writer extends AbstractAceWriter { boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIp.getProtocol() != null) { - request.mask[IP_VERSION_OFFSET] |= IP_VERSION_MASK; + request.mask[baseOffset + IP_VERSION_OFFSET] |= IP_VERSION_MASK; } if (aceIp.getDscp() != null) { aceIsEmpty = false; // DCSP (bits 4-9 of IP6 header) - request.mask[IP_VERSION_OFFSET] |= DSCP_MASK1; - request.mask[IP_VERSION_OFFSET + 1] |= DSCP_MASK2; + request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; } if (aceIp.getSourcePortRange() != null) { @@ -124,21 +128,21 @@ final class AceIp6Writer extends AbstractAceWriter { if (ipVersion.getFlowLabel() != null) { aceIsEmpty = false; // bits 12-31 - request.mask[IP_VERSION_OFFSET + 1] |= (byte) 0x0f; - request.mask[IP_VERSION_OFFSET + 2] = (byte) 0xff; - request.mask[IP_VERSION_OFFSET + 3] = (byte) 0xff; + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f; + request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff; + request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff; } if (ipVersion.getSourceIpv6Network() != null) { aceIsEmpty = false; final byte[] mask = toByteMask(ipVersion.getSourceIpv6Network()); - System.arraycopy(mask, 0, request.mask, SRC_IP_OFFSET, mask.length); + System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length); } if (ipVersion.getDestinationIpv6Network() != null) { aceIsEmpty = false; final byte[] mask = toByteMask(ipVersion.getDestinationIpv6Network()); - System.arraycopy(mask, 0, request.mask, DST_IP_OFFSET, mask.length); + System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); } if (aceIsEmpty) { @@ -157,7 +161,8 @@ final class AceIp6Writer extends AbstractAceWriter { @Override public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, @Nonnull final AceIp aceIp, - final int tableIndex) { + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); @@ -165,16 +170,19 @@ final class AceIp6Writer extends AbstractAceWriter { request.match = new byte[TABLE_MASK_LENGTH]; boolean noMatch = true; + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIp.getProtocol() != null) { - request.match[IP_VERSION_OFFSET] |= (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4)); + request.match[baseOffset + IP_VERSION_OFFSET] |= + (byte) (IP_VERSION_MASK & (aceIp.getProtocol().intValue() << 4)); } if (aceIp.getDscp() != null) { noMatch = false; final int dscp = aceIp.getDscp().getValue(); // set bits 4-9 of IP6 header: - request.match[IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dscp >> 2)); - request.match[IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dscp << 6)); + request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dscp >> 2)); + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dscp << 6)); } if (aceIp.getSourcePortRange() != null) { @@ -189,21 +197,21 @@ final class AceIp6Writer extends AbstractAceWriter { noMatch = false; final int flowLabel = ipVersion.getFlowLabel().getValue().intValue(); // bits 12-31 - request.match[IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16)); - request.match[IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8)); - request.match[IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel); + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16)); + request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8)); + request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel); } if (ipVersion.getSourceIpv6Network() != null) { noMatch = false; final byte[] match = toMatchValue(ipVersion.getSourceIpv6Network()); - System.arraycopy(match, 0, request.match, SRC_IP_OFFSET, IP6_LEN); + System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN); } if (ipVersion.getDestinationIpv6Network() != null) { noMatch = false; final byte[] match = toMatchValue(ipVersion.getDestinationIpv6Network()); - System.arraycopy(match, 0, request.match, DST_IP_OFFSET, IP6_LEN); + System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); } if (noMatch) { diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java index 2d66619fd..934318613 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java @@ -18,6 +18,7 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl; import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; import java.util.List; +import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -39,5 +40,6 @@ interface AceWriter { * @param request input_acl_set_interface request DTO */ void write(@Nonnull final InstanceIdentifier id, @Nonnull final List aces, - @Nonnull final InputAclSetInterface request) throws VppBaseCallException, WriteTimeoutException; + @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags) + throws VppBaseCallException, WriteTimeoutException; } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java index f4ba56d45..a25ddac6c 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.concurrent.CompletionStage; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.annotation.Nonnegative; import javax.annotation.Nonnull; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey; @@ -121,6 +122,12 @@ public final class IetfAClWriter { void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, @Nonnull final List acls, @Nonnull final WriteContext writeContext) throws VppBaseCallException, WriteTimeoutException { + write(id, swIfIndex, acls, writeContext, 0); + } + + void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, @Nonnull final List acls, + @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags) + throws VppBaseCallException, WriteTimeoutException { // filter ACE entries and group by AceType final Map> acesByType = acls.stream() @@ -147,7 +154,7 @@ public final class IetfAClWriter { if (aceWriter == null) { LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType); } else { - aceWriter.write(id, aces, request); + aceWriter.write(id, aces, request, numberOfTags); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/Readme.adoc b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/Readme.adoc index e59f72abe..15b1b8c27 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/Readme.adoc +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/Readme.adoc @@ -26,7 +26,4 @@ To check how ietf-acl model was translated to classify tables/session, low-level VPP classfier works in form of offsets and masks of 16B units. The offset always starts at the beginning of L2 Ethernet header of input packet. Because IP header can have variable length, -source/destination port matching (L4 features of ietf-acl model) is not possible. - -Current implementation also assumes constant Ethernet header size -(802.1Q headers are not supported). \ No newline at end of file +source/destination port matching (L4 features of ietf-acl model) is not possible. \ No newline at end of file diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java index 894269101..989aeb5d6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java @@ -18,7 +18,10 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import static io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils.getNumberOfTags; +import com.google.common.base.Optional; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils; @@ -73,8 +76,14 @@ public class SubInterfaceIetfAclCustomizer implements WriterCustomizer checkArgument(accessLists != null && accessLists.getAcl() != null, "ietf-acl container does not define acl list"); + final Optional subInterfaceOptional = + writeContext.readAfter(id.firstIdentifierOf(SubInterface.class)); + checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id); + final SubInterface subInterface = subInterfaceOptional.get(); + try { - aclWriter.write(id, subInterfaceIndex, accessLists.getAcl(), writeContext); + aclWriter.write(id, subInterfaceIndex, accessLists.getAcl(), writeContext, + getNumberOfTags(subInterface.getTags())); } catch (VppBaseCallException e) { throw new WriteFailedException.CreateFailedException(id, dataAfter, e); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/util/SubInterfaceUtils.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/util/SubInterfaceUtils.java new file mode 100644 index 000000000..acef95610 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/util/SubInterfaceUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 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.honeycomb.translate.v3po.util; + +import java.util.List; +import javax.annotation.Nonnegative; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Tags; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.tags.Tag; + +public final class SubInterfaceUtils { + + private SubInterfaceUtils() { + throw new UnsupportedOperationException("Utility class cannot be instantiated."); + } + + public static String getSubInterfaceName(final String superIfName, final int subIfaceId) { + return String.format("%s.%d", superIfName, subIfaceId); + } + + /** + * Returns number of sub-interface tags. + * + * @param tags data object that represents sub-interface tags + * @return number of sub interface tags + */ + @Nonnegative + public static int getNumberOfTags(@Nullable final Tags tags) { + if (tags == null) { + return 0; + } + final List tagList = tags.getTag(); + if (tagList == null) { + return 0; + } + return tagList.size(); + } +} diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriterTest.java index a32659a78..b5b85ded9 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriterTest.java @@ -57,7 +57,7 @@ public class AceEthWriterTest { @Test public void testGetClassifyAddDelTableRequest() throws Exception { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(action, aceEth, nextTableIndex); + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceEth, nextTableIndex, 0); assertEquals(1, request.isAdd); assertEquals(-1, request.tableIndex); @@ -81,7 +81,7 @@ public class AceEthWriterTest { @Test public void testGetClassifyAddDelSessionRequest() throws Exception { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceEth, tableIndex); + final ClassifyAddDelSession request = writer.createClassifySession(action, aceEth, tableIndex, 0); assertEquals(1, request.isAdd); assertEquals(tableIndex, request.tableIndex); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java index 95b8fc591..bc34a533c 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp4WriterTest.java @@ -16,7 +16,8 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl; -import static org.junit.Assert.assertArrayEquals; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.AceIpWriterTestUtils.assertArrayEqualsWithOffset; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; @@ -58,11 +59,8 @@ public class AceIp4WriterTest { .build(); } - @Test - public void testGetClassifyAddDelTableRequest() throws Exception { - final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex); - + private static void verifyTableRequest(final ClassifyAddDelTable request, final int nextTableIndex, + final int vlanTags) { assertEquals(1, request.isAdd); assertEquals(-1, request.tableIndex); assertEquals(1, request.nbuckets); @@ -77,14 +75,12 @@ public class AceIp4WriterTest { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - assertArrayEquals(expectedMask, request.mask); - } + assertArrayEqualsWithOffset(expectedMask, request.mask, vlanTags * VLAN_TAG_LEN); - @Test - public void testGetClassifyAddDelSessionRequest() throws Exception { - final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex); + } + private static void verifySessionRequest(final ClassifyAddDelSession request, final int tableIndex, + final int vlanTags) { assertEquals(1, request.isAdd); assertEquals(tableIndex, request.tableIndex); assertEquals(0, request.hitNextIndex); @@ -94,7 +90,55 @@ public class AceIp4WriterTest { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 1, 2, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - assertArrayEquals(expectedMatch, request.match); + assertArrayEqualsWithOffset(expectedMatch, request.match, vlanTags * VLAN_TAG_LEN); + + } + + @Test + public void testGetClassifyAddDelTableRequest() throws Exception { + final int nextTableIndex = 42; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, 0); + verifyTableRequest(request, nextTableIndex, 0); + } + + @Test + public void testGetClassifyAddDelTableRequest1VlanTag() throws Exception { + final int nextTableIndex = 42; + final int vlanTags = 1; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, vlanTags); + verifyTableRequest(request, nextTableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelTableRequest2VlanTags() throws Exception { + final int nextTableIndex = 42; + final int vlanTags = 2; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, vlanTags); + verifyTableRequest(request, nextTableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelSessionRequest() throws Exception { + final int tableIndex = 123; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, 0); + verifySessionRequest(request, tableIndex, 0); + } + + @Test + public void testGetClassifyAddDelSessionRequest1VlanTag() throws Exception { + final int tableIndex = 123; + final int vlanTags = 1; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, vlanTags); + verifySessionRequest(request, tableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelSessionRequest2VlanTags() throws Exception { + final int tableIndex = 123; + final int vlanTags = 2; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, vlanTags); + + verifySessionRequest(request, tableIndex, vlanTags); } @Test diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6WriterTest.java index 181846830..4af240c43 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIp6WriterTest.java @@ -16,7 +16,8 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl; -import static org.junit.Assert.assertArrayEquals; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.AceIpWriterTestUtils.assertArrayEqualsWithOffset; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; @@ -60,11 +61,9 @@ public class AceIp6WriterTest { .build(); } - @Test - public void testGetClassifyAddDelTableRequest() throws Exception { - final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex); + private static void verifyTableRequest(final ClassifyAddDelTable request, final int nextTableIndex, + final int vlanTags) { assertEquals(1, request.isAdd); assertEquals(-1, request.tableIndex); assertEquals(1, request.nbuckets); @@ -89,14 +88,12 @@ public class AceIp6WriterTest { // padding to multiple of 16B: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - assertArrayEquals(expectedMask, request.mask); - } + assertArrayEqualsWithOffset(expectedMask, request.mask, vlanTags * VLAN_TAG_LEN); - @Test - public void testGetClassifyAddDelSessionRequest() throws Exception { - final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex); + } + private static void verifySessionRequest(final ClassifyAddDelSession request, final int tableIndex, + final int vlanTags) { assertEquals(1, request.isAdd); assertEquals(tableIndex, request.tableIndex); assertEquals(0, request.hitNextIndex); @@ -116,7 +113,54 @@ public class AceIp6WriterTest { // padding to multiple of 16B: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - assertArrayEquals(expectedMatch, request.match); + assertArrayEqualsWithOffset(expectedMatch, request.match, vlanTags * VLAN_TAG_LEN); + + } + + @Test + public void testGetClassifyAddDelTableRequest() throws Exception { + final int nextTableIndex = 42; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, 0); + verifyTableRequest(request, nextTableIndex, 0); + } + + @Test + public void testGetClassifyAddDelTableRequest1VlanTag() throws Exception { + final int nextTableIndex = 42; + final int vlanTags = 1; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, vlanTags); + verifyTableRequest(request, nextTableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelTableRequest2VlanTag() throws Exception { + final int nextTableIndex = 42; + final int vlanTags = 2; + final ClassifyAddDelTable request = writer.createClassifyTable(action, aceIp, nextTableIndex, vlanTags); + verifyTableRequest(request, nextTableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelSessionRequest() throws Exception { + final int tableIndex = 123; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, 0); + verifySessionRequest(request, tableIndex, 0); + } + + @Test + public void testGetClassifyAddDelSessionRequest1VlanTag() throws Exception { + final int tableIndex = 123; + final int vlanTags = 1; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, vlanTags); + verifySessionRequest(request, tableIndex, vlanTags); + } + + @Test + public void testGetClassifyAddDelSessionRequest2VlanTag() throws Exception { + final int tableIndex = 123; + final int vlanTags = 2; + final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, tableIndex, vlanTags); + verifySessionRequest(request, tableIndex, vlanTags); } @Test diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIpWriterTestUtils.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIpWriterTestUtils.java new file mode 100644 index 000000000..6b176b67e --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceIpWriterTestUtils.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2016 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.honeycomb.translate.v3po.interfaces.acl; + +import static org.junit.Assert.assertArrayEquals; + +final class AceIpWriterTestUtils { + + private AceIpWriterTestUtils() { + throw new UnsupportedOperationException("This utility class cannot be instantiated"); + } + + protected static void assertArrayEqualsWithOffset(final byte[] baseExpected, final byte[] actual, + final int offset) { + byte[] expected = new byte[baseExpected.length]; + System.arraycopy(baseExpected, 0, expected, offset, expected.length - offset); + + assertArrayEquals(expected, actual); + } +} diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/util/SubinterfaceUtilsTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/util/SubinterfaceUtilsTest.java new file mode 100644 index 000000000..fb8a8066e --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/util/SubinterfaceUtilsTest.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 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.honeycomb.translate.v3po.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class SubinterfaceUtilsTest { + + @Test + public void testGetSubInterfaceName() throws Exception { + final String superIfName = "GigabitEthernet0/9/0"; + final int subIfaceId = 123; + final String expectedSubIfaceName = "GigabitEthernet0/9/0.123"; + assertEquals(expectedSubIfaceName, SubInterfaceUtils.getSubInterfaceName(superIfName, subIfaceId)); + } +} \ No newline at end of file -- cgit 1.2.3-korg