From a55da7924adda3e82f6e5be40e01084c65e93ac0 Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Mon, 10 Oct 2016 14:55:15 +0200 Subject: HONEYCOMB-233: add support for mixing L2/L3 rules In case of L2 interfaces, acls are translated into a chain of classify tables and assigned as L2 table. In case of L3 interfaces, acls are translated into ip4 and ip6 chains (eth only rules go to both chains, rest - depending on ip-version). Limitations: - it is not possible to define L3 rule without specifying ip-version (common header fields for IP4/IP6 have different offsets), - eth rules on L3 interfaces are applied only to IP traffic (vpp classfier limitation). Change-Id: I7ca2648cabad8c6e936cf71a51e06596a42891e8 Signed-off-by: Marek Gradzki --- v3po/api/src/main/yang/v3po.yang | 2 +- v3po/api/src/main/yang/vpp-acl.yang | 19 +- .../translate/v3po/IetfAClWriterProvider.java | 8 +- .../translate/v3po/InterfacesWriterFactory.java | 6 +- .../SubinterfaceAugmentationWriterFactory.java | 6 +- .../io/fd/honeycomb/translate/v3po/V3poModule.java | 4 +- .../interfaces/acl/egress/IetfAclCustomizer.java | 6 +- .../acl/egress/SubInterfaceIetfAclCustomizer.java | 6 +- .../interfaces/acl/ingress/AbstractAceWriter.java | 190 ------------- .../v3po/interfaces/acl/ingress/AceEthWriter.java | 116 ++------ .../v3po/interfaces/acl/ingress/AceIp4Writer.java | 158 ++--------- .../v3po/interfaces/acl/ingress/AceIp6Writer.java | 191 ++----------- .../interfaces/acl/ingress/AceIpAndEthWriter.java | 120 ++++++++ .../v3po/interfaces/acl/ingress/AceWriter.java | 46 ++-- .../v3po/interfaces/acl/ingress/AclTranslator.java | 64 +++++ .../v3po/interfaces/acl/ingress/IetfAClWriter.java | 207 -------------- .../interfaces/acl/ingress/IetfAclCustomizer.java | 4 +- .../v3po/interfaces/acl/ingress/IetfAclWriter.java | 306 +++++++++++++++++++++ .../interfaces/acl/ingress/Ip4AclTranslator.java | 135 +++++++++ .../interfaces/acl/ingress/Ip6AclTranslator.java | 169 ++++++++++++ .../interfaces/acl/ingress/L2AclTranslator.java | 90 ++++++ .../acl/ingress/SubInterfaceIetfAclCustomizer.java | 4 +- .../interfaces/acl/ingress/AceEthWriterTest.java | 31 +-- .../interfaces/acl/ingress/AceIp4WriterTest.java | 41 +-- .../interfaces/acl/ingress/AceIp6WriterTest.java | 41 +-- .../acl/ingress/AceIpAndEthWriterTest.java | 114 ++++++++ .../acl/ingress/IetfAclCustomizerTest.java | 2 +- .../ingress/SubInterfaceIetfAclCustomizerTest.java | 5 +- 28 files changed, 1148 insertions(+), 943 deletions(-) delete mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java delete mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java create mode 100644 v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java create mode 100644 v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java (limited to 'v3po') diff --git a/v3po/api/src/main/yang/v3po.yang b/v3po/api/src/main/yang/v3po.yang index b2d0a74ed..a5ccec5f9 100644 --- a/v3po/api/src/main/yang/v3po.yang +++ b/v3po/api/src/main/yang/v3po.yang @@ -7,7 +7,7 @@ module v3po { description "This revision adds the following new features: - ingress/egress ACLs support - - default-action and interface-mode type as a part of ietf-acl configuration"; + - moved ACL definitions to vpp-acl module"; } revision "2015-01-05" { diff --git a/v3po/api/src/main/yang/vpp-acl.yang b/v3po/api/src/main/yang/vpp-acl.yang index 76e1eda9e..d0d24c98a 100644 --- a/v3po/api/src/main/yang/vpp-acl.yang +++ b/v3po/api/src/main/yang/vpp-acl.yang @@ -70,11 +70,13 @@ module vpp-acl { container access-lists { description - "Defines references to ietf-acl lists. Before assignment to interface, - ACL lists are merged into 3 type of acls (l2, ip4 and ip6) that are supported by vpp. - Then 3 corresponding chains of tables and sessions are created and assigned to the interface - as l2, ip4 and ip6 classify table chains. - User ordering is preserved in each group separately. + "Defines references to ietf-acl lists. + ACLs are translated into classify tables and sessions when assigned to interface. + + In case of L2 interfaces, acls are translated into a chain of classify tables and assigned as L2 table. + In case of L3 interfaces, acls are translated into ip4 and ip6 chains (eth only rules go to both chains, + rest - depending on ip-version). + User ordering is preserved in both cases. Assignment update/delete removes all created tables and sessions and repeats process described above. Update/delete of ACL lists referenced here is not permitted (assignment needs to be removed first). @@ -85,10 +87,11 @@ module vpp-acl { Limitations (due to vpp limitations): - egress rules are currently ignored (HONEYCOMB-234) - L4 rules are currently not supported (limited support will by provided by HONEYCOMB-218) - - mixing L2/L3/L4 rules is currently not supported (limited support will by provided by HONEYCOMB-233) - - L2 only rules on L3 interfaces are not supported (not allowed by vpp, - in the future defining L2/L3 pairs should be partially supported) + - mixing L2 and L3 rules is possible only if ace-ip-version is provided + (vpp classfier api limitation: common header fields for IP4/IP6 have different offsets) + - L2 rules on L3 interfaces only to IP traffic (vpp classfier limitation) - vlan tags are supported only for sub-interfaces defined as exact-match"; + list acl { key "type name"; ordered-by user; diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java index 8e001b75d..301c30501 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java @@ -18,10 +18,10 @@ package io.fd.honeycomb.translate.v3po; import com.google.inject.Inject; import com.google.inject.Provider; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.vpp.jvpp.core.future.FutureJVppCore; -class IetfAClWriterProvider implements Provider { +class IetfAClWriterProvider implements Provider { private final FutureJVppCore jvpp; @@ -31,7 +31,7 @@ class IetfAClWriterProvider implements Provider { } @Override - public IetfAClWriter get() { - return new IetfAClWriter(jvpp); + public IetfAclWriter get() { + return new IetfAclWriter(jvpp); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java index 9c46ba316..be3801bd2 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java @@ -35,7 +35,7 @@ import io.fd.honeycomb.translate.v3po.interfaces.VhostUserCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.VxlanCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.VxlanGpeCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4AddressCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4Customizer; @@ -94,7 +94,7 @@ public final class InterfacesWriterFactory implements WriterFactory { org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Egress.class); private final FutureJVppCore jvpp; - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext bdNamingContext; private final NamingContext ifcNamingContext; private final VppClassifierContextManager classifyTableContext; @@ -102,7 +102,7 @@ public final class InterfacesWriterFactory implements WriterFactory { @Inject public InterfacesWriterFactory(final FutureJVppCore vppJvppIfcDependency, - final IetfAClWriter aclWriter, + final IetfAclWriter aclWriter, @Named("bridge-domain-context") final NamingContext bridgeDomainContextDependency, @Named("interface-context") final NamingContext interfaceContextDependency, @Named("classify-table-context") final VppClassifierContextManager classifyTableContext, diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java index f4dc07646..afa362d5c 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java @@ -26,7 +26,7 @@ import io.fd.honeycomb.translate.v3po.interfaces.RewriteCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.SubInterfaceAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.SubInterfaceCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.SubInterfaceL2Customizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.SubInterfaceIetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.SubInterfaceIpv4AddressCustomizer; import io.fd.honeycomb.translate.vpp.util.NamingContext; @@ -59,7 +59,7 @@ import io.fd.vpp.jvpp.core.future.FutureJVppCore; public final class SubinterfaceAugmentationWriterFactory implements WriterFactory { private final FutureJVppCore jvpp; - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext ifcContext; private final NamingContext bdContext; private final VppClassifierContextManager classifyTableContext; @@ -79,7 +79,7 @@ public final class SubinterfaceAugmentationWriterFactory implements WriterFactor org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress.class); public SubinterfaceAugmentationWriterFactory(final FutureJVppCore jvpp, - final IetfAClWriter aclWriter, + final IetfAclWriter aclWriter, final NamingContext ifcContext, final NamingContext bdContext, final VppClassifierContextManager classifyTableContext) { this.jvpp = jvpp; this.aclWriter = aclWriter; diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java index de29d5d2c..eaecdf025 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java @@ -26,7 +26,7 @@ import io.fd.honeycomb.translate.v3po.cfgattrs.V3poConfiguration; import io.fd.honeycomb.translate.v3po.initializers.InterfacesInitializer; import io.fd.honeycomb.translate.v3po.initializers.VppClassifierInitializer; import io.fd.honeycomb.translate.v3po.initializers.VppInitializer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.notification.InterfaceChangeNotificationProducer; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.v3po.vppclassifier.VppClassifierContextManager; @@ -59,7 +59,7 @@ public class V3poModule extends AbstractModule { bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(1)); // Utils - bind(IetfAClWriter.class).toProvider(IetfAClWriterProvider.class); + bind(IetfAclWriter.class).toProvider(IetfAClWriterProvider.class); // Context utility for deleted interfaces bind(DisabledInterfacesManager.class).toInstance(new DisabledInterfacesManager()); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java index 0eff83b00..3dc9e2262 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java @@ -19,7 +19,7 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.egress; import static com.google.common.base.Preconditions.checkNotNull; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; @@ -31,11 +31,11 @@ import org.slf4j.LoggerFactory; public class IetfAclCustomizer implements WriterCustomizer { private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public IetfAclCustomizer(final IetfAClWriter aclWriter, final NamingContext interfaceContext) { + public IetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java index 2a20a754f..b5198465a 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java @@ -19,7 +19,7 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.egress; import static com.google.common.base.Preconditions.checkNotNull; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; @@ -31,10 +31,10 @@ import org.slf4j.LoggerFactory; public class SubInterfaceIetfAclCustomizer implements WriterCustomizer { private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public SubInterfaceIetfAclCustomizer(final IetfAClWriter aclWriter, final NamingContext interfaceContext) { + public SubInterfaceIetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java deleted file mode 100644 index a5bbab545..000000000 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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.ingress; - -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.vpp.util.JvppReplyConsumer; -import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; -import java.util.List; -import java.util.ListIterator; -import java.util.concurrent.CompletionStage; -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -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; -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.packet.handling.Permit; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Base writer for translation of ietf-acl model ACEs to VPP's classify tables and sessions.

Creates one classify - * table with single session per ACE. - * - * @param type of access control list entry - */ -abstract class AbstractAceWriter implements AceWriter, JvppReplyConsumer { - - // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model - // mapping more convenient): - // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table, - // 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 int PERMIT = -1; - private static final int DENY = 0; - - private final FutureJVppCore futureJVppCore; - - public AbstractAceWriter(@Nonnull final FutureJVppCore futureJVppCore) { - this.futureJVppCore = checkNotNull(futureJVppCore, "futureJVppCore should not be null"); - } - - /** - * Creates classify table for given ACE. - * - * @param ace ACE to be translated - * @param mode interface mode - * @param nextTableIndex classify table index - * @param vlanTags number of vlan tags - * @return classify table that represents given ACE - */ - protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final T ace, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags); - - /** - * Creates classify session for given ACE. - * - * @param action packet handling action (permit/deny) - * @param ace ACE to be translated - * @param mode interface mode - * @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, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags); - - /** - * Sets classify table index for input_acl_set_interface request. - * - * @param request request DTO - * @param tableIndex pointer to a chain of classify tables - */ - protected abstract void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex); - - @Override - public final void write(@Nonnull final InstanceIdentifier id, @Nonnull final List aces, - final InterfaceMode mode, final AccessLists.DefaultAction defaultAction, - @Nonnull final InputAclSetInterface request, - @Nonnegative final int vlanTags) - throws WriteFailedException { - - checkArgument(vlanTags >= 0 && vlanTags <= 2, "Number of vlan tags %s is not in [0,2] range"); - int nextTableIndex = configureDefaltAction(id, defaultAction); - - final ListIterator iterator = aces.listIterator(aces.size()); - while (iterator.hasPrevious()) { - // Create table + session per entry - final Ace ace = iterator.previous(); - final PacketHandling action = ace.getActions().getPacketHandling(); - final T type = (T)ace.getMatches().getAceType(); - final ClassifyAddDelTable ctRequest = createClassifyTable(type, mode, nextTableIndex, vlanTags); - nextTableIndex = createClassifyTable(id, ctRequest); - createClassifySession(id, createClassifySession(action, type, mode, nextTableIndex, vlanTags)); - } - setClassifyTable(request, nextTableIndex); - } - - private int configureDefaltAction(@Nonnull final InstanceIdentifier id, final AccessLists.DefaultAction defaultAction) - throws WriteFailedException { - ClassifyAddDelTable ctRequest = createClassifyTable(-1); - if (AccessLists.DefaultAction.Permit.equals(defaultAction)) { - ctRequest.missNextIndex = PERMIT; - } else { - ctRequest.missNextIndex = DENY; - } - ctRequest.mask = new byte[16]; - ctRequest.skipNVectors = 0; - ctRequest.matchNVectors = 1; - return createClassifyTable(id, ctRequest); - } - - private int createClassifyTable(@Nonnull final InstanceIdentifier id, - @Nonnull final ClassifyAddDelTable request) - throws WriteFailedException { - final CompletionStage cs = futureJVppCore.classifyAddDelTable(request); - - final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id); - return reply.newTableIndex; - } - - private void createClassifySession(@Nonnull final InstanceIdentifier id, - @Nonnull final ClassifyAddDelSession request) - throws WriteFailedException { - final CompletionStage cs = futureJVppCore.classifyAddDelSession(request); - - getReplyForWrite(cs.toCompletableFuture(), id); - } - - protected ClassifyAddDelTable createClassifyTable(final int nextTableIndex) { - final ClassifyAddDelTable request = new ClassifyAddDelTable(); - request.isAdd = 1; - request.tableIndex = -1; // value not present - request.nbuckets = 1; // we expect exactly one session per table - request.nextTableIndex = nextTableIndex; - request.memorySize = TABLE_MEM_SIZE; - request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain - return request; - } - - protected ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, final int tableIndex) { - final ClassifyAddDelSession request = new ClassifyAddDelSession(); - request.isAdd = 1; - request.tableIndex = tableIndex; - request.opaqueIndex = ~0; // value not used - - if (action instanceof Permit) { - request.hitNextIndex = -1; - } // deny (0) is default value - - 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/ingress/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java index 939e4eb98..91ab927fb 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java @@ -16,15 +16,9 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static com.google.common.base.Preconditions.checkArgument; - import com.google.common.annotations.VisibleForTesting; -import io.fd.honeycomb.translate.vpp.util.MacTranslator; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; -import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; 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; @@ -33,69 +27,29 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceEthWriter extends AbstractAceWriter implements MacTranslator { +final class AceEthWriter implements AceWriter, AclTranslator, L2AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 1; private static final Logger LOG = LoggerFactory.getLogger(AceEthWriter.class); - public AceEthWriter(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static void checkInterfaceMode(@Nullable final InterfaceMode mode) { - checkArgument(InterfaceMode.L2.equals(mode), "L2 rules are not allowed for interface in L3 mode"); - } - @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceEth aceEth, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { - checkInterfaceMode(mode); - - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + public ClassifyAddDelTable createTable(@Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { + final ClassifyAddDelTable request = createTable(nextTableIndex); request.mask = new byte[16]; - boolean aceIsEmpty = true; - - // destination-mac-address or destination-mac-address-mask is present => - // ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00 - if (aceEth.getDestinationMacAddressMask() != null) { - aceIsEmpty = false; - final String macAddress = aceEth.getDestinationMacAddressMask().getValue(); - final List parts = COLON_SPLITTER.splitToList(macAddress); - int i = 0; - for (String part : parts) { - request.mask[i++] = parseHexByte(part); - } - } else if (aceEth.getDestinationMacAddress() != null) { - aceIsEmpty = false; - for (int i = 0; i < 6; ++i) { - request.mask[i] = (byte) 0xff; - } - } - - // source-mac-address or source-mac-address-mask => - // 00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00 - if (aceEth.getSourceMacAddressMask() != null) { - aceIsEmpty = false; - final String macAddress = aceEth.getSourceMacAddressMask().getValue(); - final List parts = COLON_SPLITTER.splitToList(macAddress); - int i = 6; - for (String part : parts) { - request.mask[i++] = parseHexByte(part); - } - } else if (aceEth.getSourceMacAddress() != null) { - aceIsEmpty = false; - for (int i = 6; i < 12; ++i) { - request.mask[i] = (byte) 0xff; - } - } + boolean aceIsEmpty = + destinationMacAddressMask(aceEth.getDestinationMacAddressMask(), aceEth.getDestinationMacAddress(), + request); + aceIsEmpty &= + sourceMacAddressMask(aceEth.getSourceMacAddressMask(), aceEth.getSourceMacAddress(), request); if (aceIsEmpty) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceEth.toString())); + String.format("Ace %s does not define packet field match values", aceEth.toString())); } request.skipNVectors = 0; @@ -106,50 +60,24 @@ final class AceEthWriter extends AbstractAceWriter implements MacTransla } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceEth aceEth, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { - checkInterfaceMode(mode); - - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[16]; - boolean noMatch = true; - - if (aceEth.getDestinationMacAddress() != null) { - noMatch = false; - final String macAddress = aceEth.getDestinationMacAddress().getValue(); - final List parts = COLON_SPLITTER.splitToList(macAddress); - int i = 0; - for (String part : parts) { - request.match[i++] = parseHexByte(part); - } - } - - if (aceEth.getSourceMacAddress() != null) { - noMatch = false; - final String macAddress = aceEth.getSourceMacAddress().getValue(); - final List parts = COLON_SPLITTER.splitToList(macAddress); - int i = 6; - for (String part : parts) { - request.match[i++] = parseHexByte(part); - } - } + boolean noMatch = destinationMacAddressMatch(aceEth.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(aceEth.getSourceMacAddress(), request); if (noMatch) { throw new IllegalArgumentException( - String.format("Ace %s does not define neither source nor destination MAC address", - aceEth.toString())); + String.format("Ace %s does not define neither source nor destination MAC address", + aceEth.toString())); } LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceEth, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.l2TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java index 2f8d030ae..affc8735d 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java @@ -19,121 +19,42 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Ints; -import io.fd.honeycomb.translate.vpp.util.Ipv4Translator; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import javax.annotation.Nonnull; import javax.annotation.Nullable; 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; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceIp4Writer extends AbstractAceWriter implements Ipv4Translator { +final class AceIp4Writer implements AceWriter, AclTranslator, Ip4AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 3; // number of 16B vectors - private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class); private static final int TABLE_MASK_LENGTH = 48; - private static final int IP4_MASK_BIT_LENGTH = 32; - - private static final int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) - private static final int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET+2; - private static final int DSCP_OFFSET = 15; - private static final int DSCP_MASK = 0xfc; - private static final int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET+9; - private static final int IP_PROTOCOL_MASK = 0xff; - private static final int IP4_LEN = 4; - private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 12; - private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN; - - public AceIp4Writer(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static byte[] toByteMask(final int prefixLength) { - final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength); - return Ints.toByteArray((int) mask); - } - - private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) { - final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]); - return toByteMask(prefixLength); - } - - // static removed, cant use default from static content - private byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) { - final String[] split = ipv4Prefix.getValue().split("/"); - final byte[] addressBytes = ipv4AddressNoZoneToArray(split[0]); - final byte[] mask = toByteMask(Byte.valueOf(split[1])); - for (int i = 0; i < addressBytes.length; ++i) { - addressBytes[i] &= mask[i]; - } - return addressBytes; - } + private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class); @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + final ClassifyAddDelTable request = createTable(nextTableIndex); request.skipNVectors = 0; // match entire L2 and L3 header request.matchNVectors = MATCH_N_VECTORS; - - boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // in L2 mode we need to match ether type - request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; - request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; - } - - if (aceIp.getDscp() != null) { - aceIsEmpty = false; - request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits - } - - if (aceIp.getProtocol() != null) { // Internet Protocol number - request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getSourceIpv4Network() != null) { - aceIsEmpty = false; - 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, - baseOffset + DST_IP_OFFSET, IP4_LEN); - } - + boolean aceIsEmpty = ip4Mask(baseOffset, mode, aceIp, ipVersion, request, LOG); if (aceIsEmpty) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceIp.toString())); + String.format("Ace %s does not define packet field match values", aceIp.toString())); } LOG.debug("ACE rule={} translated to table={}.", aceIp, request); @@ -141,70 +62,25 @@ final class AceIp4Writer extends AbstractAceWriter implements Ipv4Transla } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); - + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[TABLE_MASK_LENGTH]; - boolean noMatch = true; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // match IP4 etherType (0x0800) - request.match[baseOffset + ETHER_TYPE_OFFSET] = 0x08; - request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = 0x00; - } - - if (aceIp.getProtocol() != null) { - request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & aceIp.getProtocol()); - } - - if (aceIp.getDscp() != null) { - noMatch = false; - request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2)); - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getSourceIpv4Network() != null) { - noMatch = false; - 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, - baseOffset + DST_IP_OFFSET, - IP4_LEN); - } - + boolean noMatch = ip4Match(baseOffset, mode, aceIp, ipVersion, request, LOG); if (noMatch) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceIp.toString())); + String.format("Ace %s does not define packet field match values", aceIp.toString())); } LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.ip4TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java index f1cccba92..b1186440d 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java @@ -19,142 +19,39 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.VisibleForTesting; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.BitSet; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; import javax.annotation.Nonnull; import javax.annotation.Nullable; 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; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceIp6Writer extends AbstractAceWriter { +final class AceIp6Writer implements AceWriter, AclTranslator, Ip6AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 4; // number of 16B vectors - private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class); private static final int TABLE_MASK_LENGTH = 64; - private static final int IP6_MASK_BIT_LENGTH = 128; - - private static final int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) - private static final int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET+2; - private static final int DSCP_MASK1 = 0x0f; - private static final int DSCP_MASK2 = 0xc0; - private static final int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET+6; - private static final int IP_PROTOCOL_MASK = 0xff; - private static final int IP6_LEN = 16; - private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8; - private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN; - - public AceIp6Writer(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static byte[] toByteMask(final int prefixLength) { - final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH); - mask.set(0, prefixLength, true); - if (prefixLength < IP6_MASK_BIT_LENGTH) { - mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false); - } - return mask.toByteArray(); - } - - private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) { - final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]); - return toByteMask(prefixLength); - } - - private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) { - final String[] split = ipv6Prefix.getValue().split("/"); - final byte[] addressBytes; - try { - addressBytes = InetAddress.getByName(split[0]).getAddress(); - } catch (UnknownHostException e) { - throw new IllegalArgumentException("Invalid IP6 address", e); - } - final byte[] mask = toByteMask(Short.valueOf(split[1])); - int pos = 0; - for (; pos < mask.length; ++pos) { - addressBytes[pos] &= mask[pos]; - } - // mask can be shorter that address, so we need to clear rest of the address: - for (; pos < addressBytes.length; ++pos) { - addressBytes[pos] = 0; - } - return addressBytes; - } + private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class); @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + final ClassifyAddDelTable request = createTable(nextTableIndex); request.skipNVectors = 0; // match entire L2 and L3 header request.matchNVectors = MATCH_N_VECTORS; - - boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // in L2 mode we need to match ether type - request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; - request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; - } - - if (aceIp.getDscp() != null) { - aceIsEmpty = false; - // DCSP (bits 4-9 of IP6 header) - request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; - request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; - } - - if (aceIp.getProtocol() != null) { - aceIsEmpty = false; - request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getFlowLabel() != null) { - aceIsEmpty = false; - // bits 12-31 - 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, baseOffset + SRC_IP_OFFSET, mask.length); - } - - if (ipVersion.getDestinationIpv6Network() != null) { - aceIsEmpty = false; - final byte[] mask = toByteMask(ipVersion.getDestinationIpv6Network()); - System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); - } - + boolean aceIsEmpty = ip6Mask(baseOffset, mode, aceIp, ipVersion, request, LOG); if (aceIsEmpty) { throw new IllegalArgumentException( String.format("Ace %s does not define packet field match values", aceIp.toString())); @@ -165,68 +62,19 @@ final class AceIp6Writer extends AbstractAceWriter { } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[TABLE_MASK_LENGTH]; - boolean noMatch = true; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // match IP6 etherType (0x86dd) - request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86; - request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd; - } - - if (aceIp.getDscp() != null) { - noMatch = false; - final int dscp = aceIp.getDscp().getValue(); - // set bits 4-9 of IP6 header: - 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.getProtocol() != null) { - noMatch = false; - request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & aceIp.getProtocol()); - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getFlowLabel() != null) { - noMatch = false; - final int flowLabel = ipVersion.getFlowLabel().getValue().intValue(); - // bits 12-31 - 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, baseOffset + SRC_IP_OFFSET, IP6_LEN); - } - - if (ipVersion.getDestinationIpv6Network() != null) { - noMatch = false; - final byte[] match = toMatchValue(ipVersion.getDestinationIpv6Network()); - System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); - } - + boolean noMatch = ip6Match(baseOffset, mode, aceIp, ipVersion, request, LOG); if (noMatch) { throw new IllegalArgumentException( String.format("Ace %s does not define packet field match values", aceIp.toString())); @@ -235,9 +83,4 @@ final class AceIp6Writer extends AbstractAceWriter { LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.ip6TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java new file mode 100644 index 000000000..40a050a6d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java @@ -0,0 +1,120 @@ +/* + * 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.ingress; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +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; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.AceIpVersion; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class AceIpAndEthWriter + implements AceWriter, AclTranslator, L2AclTranslator, Ip4AclTranslator, Ip6AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AceIpAndEthWriter.class); + + private static int maskLength(@Nonnull final AceIpAndEth ace) { + if (ace.getAceIpVersion() != null) { + if (ace.getAceIpVersion() instanceof AceIpv4) { + return 48; + } else { + return 64; + } + } + return 16; + } + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceIpAndEth ace, @Nullable final InterfaceMode mode, + final int nextTableIndex, final int vlanTags) { + final ClassifyAddDelTable request = createTable(nextTableIndex); + final int maskLength = maskLength(ace); + request.mask = new byte[maskLength]; + request.skipNVectors = 0; + request.matchNVectors = maskLength / 16; + + boolean aceIsEmpty = + destinationMacAddressMask(ace.getDestinationMacAddressMask(), ace.getDestinationMacAddress(), request); + aceIsEmpty &= sourceMacAddressMask(ace.getSourceMacAddressMask(), ace.getSourceMacAddress(), request); + + // if we use classifier API, we need to know ip version (fields common for ip4 and ip6 have different offsets): + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + aceIsEmpty &= ip4Mask(baseOffset, mode, ace, ipVersion, request, LOG); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + aceIsEmpty &= ip6Mask(baseOffset, mode, ace, ipVersion, request, LOG); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + + LOG.debug("ACE rule={} translated to table={}.", ace, request); + return request; + } + + @Override + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIpAndEth ace, + @Nullable final InterfaceMode mode, final int tableIndex, + final int vlanTags) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + request.match = new byte[maskLength(ace)]; + + boolean noMatch = destinationMacAddressMatch(ace.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(ace.getSourceMacAddress(), request); + + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + noMatch &= ip4Match(baseOffset, mode, ace, ipVersion, request, LOG); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + noMatch &= ip6Match(baseOffset, mode, ace, ipVersion, request, LOG); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + + LOG.debug("ACE action={}, rule={} translated to session={}.", action, ace, request); + return request; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java index 7ba44f166..790b6d664 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java @@ -16,34 +16,38 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import java.util.List; -import javax.annotation.Nonnegative; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; 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 javax.annotation.Nullable; +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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** * Writer responsible for translation of ietf-acl model ACEs to VPP's classify tables and sessions. + * + * @param type of access control list entry */ -interface AceWriter { +interface AceWriter { + /** + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param nextTableIndex index of the next classify table in chain + * @param vlanTags number of vlan tags + */ + @Nonnull + ClassifyAddDelTable createTable(@Nonnull final T ace, @Nullable final InterfaceMode mode, final int nextTableIndex, + final int vlanTags); /** - * Translates list of ACEs to chain of classify tables. Each ACE is translated into one classify table with single - * classify session. Also initializes input_acl_set_interface request message DTO with first classify table of the - * chain that was created. - * - * @param id uniquely identifies ietf-acl container - * @param aces list of access control entries - * @param mode interface mode (L2/L3) - * @param defaultAction to be taken when packet that does not match any of rules defined in - * @param request input_acl_set_interface request DTO + * @param action to be taken when packet does match the specified ace + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param tableIndex index of corresponding classify table + * @param vlanTags number of vlan tags */ - void write(@Nonnull final InstanceIdentifier id, @Nonnull final List aces, - final InterfaceMode mode, final AccessLists.DefaultAction defaultAction, - @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags) - throws WriteFailedException; + @Nonnull + ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, @Nonnull T ace, + @Nullable final InterfaceMode mode, final int tableIndex, final int vlanTags); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java new file mode 100644 index 000000000..9a931a923 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java @@ -0,0 +1,64 @@ +/* + * 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.ingress; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +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.actions.PacketHandling; +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.packet.handling.Permit; + +/** + * Utility that helps translating of ietf-acl model ACEs to VPP's classify tables and sessions. + */ +interface AclTranslator { + + // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model + // mapping more convenient): + // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table, + // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, so setting 8K for now + int TABLE_MEM_SIZE = 8 * 1024; + int VLAN_TAG_LEN = 4; + + default ClassifyAddDelTable createTable(final int nextTableIndex) { + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = 1; + request.tableIndex = -1; // value not present + request.nbuckets = 1; // we expect exactly one session per table + request.nextTableIndex = nextTableIndex; + request.memorySize = TABLE_MEM_SIZE; + request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain + return request; + } + + default ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, final int tableIndex) { + final ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + request.opaqueIndex = ~0; // value not used + + if (action instanceof Permit) { + request.hitNextIndex = -1; + } // deny (0) is default value + + return request; + } + + default 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/ingress/IetfAClWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java deleted file mode 100644 index 2fd00e39e..000000000 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * 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.ingress; - -import static com.google.common.base.Preconditions.checkArgument; - -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; -import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter; -import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer; -import io.fd.honeycomb.translate.write.WriteContext; -import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; -import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface; -import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; -import java.util.HashMap; -import java.util.List; -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 javax.annotation.Nullable; -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; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries; -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.matches.AceType; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public final class IetfAClWriter implements JvppReplyConsumer { - - private static final Logger LOG = LoggerFactory.getLogger(IetfAClWriter.class); - private final FutureJVppCore jvpp; - - private Map aceWriters = new HashMap<>(); - - public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) { - this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null"); - aceWriters.put(AclType.ETH, new AceEthWriter(futureJVppCore)); - aceWriters.put(AclType.IP4, new AceIp4Writer(futureJVppCore)); - aceWriters.put(AclType.IP6, new AceIp6Writer(futureJVppCore)); - } - - private static Stream aclToAceStream(@Nonnull final Acl assignedAcl, - @Nonnull final WriteContext writeContext) { - final String aclName = assignedAcl.getName(); - final Class aclType = assignedAcl.getType(); - - // ietf-acl updates are handled first, so we use writeContext.readAfter - final Optional - aclOptional = writeContext.readAfter(IetfAclWriter.ACL_ID.child( - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, - new AclKey(aclName, aclType))); - checkArgument(aclOptional.isPresent(), "Acl lists not configured"); - final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl - acl = aclOptional.get(); - - final AccessListEntries accessListEntries = acl.getAccessListEntries(); - checkArgument(accessListEntries != null, "access list entries not configured"); - - return accessListEntries.getAce().stream(); - } - - void deleteAcl(@Nonnull final InstanceIdentifier id, final int swIfIndex) - throws WriteFailedException { - final ClassifyTableByInterface request = new ClassifyTableByInterface(); - request.swIfIndex = swIfIndex; - - final CompletionStage cs = jvpp.classifyTableByInterface(request); - final ClassifyTableByInterfaceReply reply = getReplyForDelete(cs.toCompletableFuture(), id); - - // We unassign and remove all ACL-related classify tables for given interface (we assume we are the only - // classify table manager) - - unassignClassifyTables(id, reply); - - removeClassifyTable(id, reply.l2TableId); - removeClassifyTable(id, reply.ip4TableId); - removeClassifyTable(id, reply.ip6TableId); - } - - private void unassignClassifyTables(@Nonnull final InstanceIdentifier id, - final ClassifyTableByInterfaceReply currentState) - throws WriteFailedException { - final InputAclSetInterface request = new InputAclSetInterface(); - request.isAdd = 0; - request.swIfIndex = currentState.swIfIndex; - request.l2TableIndex = currentState.l2TableId; - request.ip4TableIndex = currentState.ip4TableId; - request.ip6TableIndex = currentState.ip6TableId; - final CompletionStage inputAclSetInterfaceReplyCompletionStage = - jvpp.inputAclSetInterface(request); - getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); - } - - private void removeClassifyTable(@Nonnull final InstanceIdentifier id, final int tableIndex) - throws WriteFailedException { - - if (tableIndex == -1) { - return; // classify table id is absent - } - final ClassifyAddDelTable request = new ClassifyAddDelTable(); - request.tableIndex = tableIndex; - final CompletionStage cs = jvpp.classifyAddDelTable(request); - getReplyForDelete(cs.toCompletableFuture(), id); - } - - void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, @Nonnull final List acls, - final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext) - throws WriteFailedException { - write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0); - } - - void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, final InterfaceMode mode, - @Nonnull final List acls, final AccessLists.DefaultAction defaultAction, - @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags) - throws WriteFailedException { - - // filter ACE entries and group by AceType - final Map> acesByType = acls.stream() - .flatMap(acl -> aclToAceStream(acl, writeContext)) - .collect(Collectors.groupingBy(AclType::fromAce)); - - final InputAclSetInterface request = new InputAclSetInterface(); - request.isAdd = 1; - request.swIfIndex = swIfIndex; - request.l2TableIndex = -1; - request.ip4TableIndex = -1; - request.ip6TableIndex = -1; - - // for each AceType: - for (Map.Entry> entry : acesByType.entrySet()) { - final AclType aceType = entry.getKey(); - final List aces = entry.getValue(); - LOG.trace("Processing ACEs of {} type: {}", aceType, aces); - - final AceWriter aceWriter = aceWriters.get(aceType); - if (aceWriter == null) { - LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType); - } else { - aceWriter.write(id, aces, mode, defaultAction, request, numberOfTags); - } - } - - final CompletionStage inputAclSetInterfaceReplyCompletionStage = - jvpp.inputAclSetInterface(request); - getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); - } - - private enum AclType { - ETH, IP4, IP6; - - @Nonnull - private static AclType fromAce(final Ace ace) { - AclType result = null; - final AceType aceType; - try { - aceType = ace.getMatches().getAceType(); - if (aceType instanceof AceEth) { - result = ETH; - } else if (aceType instanceof AceIp) { - final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion(); - if (aceIpVersion instanceof AceIpv4) { - result = IP4; - } else { - result = IP6; - } - } - } catch (NullPointerException e) { - throw new IllegalArgumentException("Incomplete ACE: " + ace, e); - } - if (result == null) { - throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType)); - } - return result; - } - } -} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java index 9074684d7..4eac0fad6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java @@ -41,10 +41,10 @@ import org.slf4j.LoggerFactory; public class IetfAclCustomizer implements WriterCustomizer { private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public IetfAclCustomizer(@Nonnull final IetfAClWriter aclWriter, + public IetfAclCustomizer(@Nonnull final IetfAclWriter aclWriter, @Nonnull final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java new file mode 100644 index 000000000..5814211f4 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java @@ -0,0 +1,306 @@ +/* + * 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.ingress; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface; +import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterfaceReply; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; +import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply; +import io.fd.vpp.jvpp.core.future.FutureJVppCore; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries; +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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class IetfAclWriter implements JvppReplyConsumer, AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class); + private static final int NOT_DEFINED = -1; + private final FutureJVppCore jvpp; + + private Map> aceWriters = new HashMap<>(); + + public IetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) { + this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null"); + aceWriters.put(AclType.ETH, new AceEthWriter()); + aceWriters.put(AclType.IP4, new AceIp4Writer()); + aceWriters.put(AclType.IP6, new AceIp6Writer()); + aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter()); + } + + private static Stream aclToAceStream(@Nonnull final Acl assignedAcl, + @Nonnull final WriteContext writeContext) { + final String aclName = assignedAcl.getName(); + final Class aclType = assignedAcl.getType(); + + // ietf-acl updates are handled first, so we use writeContext.readAfter + final Optional + aclOptional = writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, + new AclKey(aclName, aclType))); + checkArgument(aclOptional.isPresent(), "Acl lists not configured"); + final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl + acl = aclOptional.get(); + + final AccessListEntries accessListEntries = acl.getAccessListEntries(); + checkArgument(accessListEntries != null, "access list entries not configured"); + + return accessListEntries.getAce().stream(); + } + + void deleteAcl(@Nonnull final InstanceIdentifier id, final int swIfIndex) + throws WriteFailedException { + final ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = swIfIndex; + + final CompletionStage cs = jvpp.classifyTableByInterface(request); + final ClassifyTableByInterfaceReply reply = getReplyForDelete(cs.toCompletableFuture(), id); + + // We unassign and remove all ACL-related classify tables for given interface (we assume we are the only + // classify table manager) + + unassignClassifyTables(id, reply); + + removeClassifyTable(id, reply.l2TableId); + removeClassifyTable(id, reply.ip4TableId); + removeClassifyTable(id, reply.ip6TableId); + } + + private void unassignClassifyTables(@Nonnull final InstanceIdentifier id, + final ClassifyTableByInterfaceReply currentState) + throws WriteFailedException { + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 0; + request.swIfIndex = currentState.swIfIndex; + request.l2TableIndex = currentState.l2TableId; + request.ip4TableIndex = currentState.ip4TableId; + request.ip6TableIndex = currentState.ip6TableId; + final CompletionStage inputAclSetInterfaceReplyCompletionStage = + jvpp.inputAclSetInterface(request); + getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + } + + private void removeClassifyTable(@Nonnull final InstanceIdentifier id, final int tableIndex) + throws WriteFailedException { + + if (tableIndex == -1) { + return; // classify table id is absent + } + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.tableIndex = tableIndex; + final CompletionStage cs = jvpp.classifyAddDelTable(request); + getReplyForDelete(cs.toCompletableFuture(), id); + } + + void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, @Nonnull final List acls, + final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0); + } + + private static boolean appliesToIp4Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv4) { + return true; + } + if (aceType instanceof AceEth) { + return true; // L2 only rules are possible for IP4 traffic + } + if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) { + return true; + } + return false; + } + + private static boolean appliesToIp6Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv6) { + return true; + } + if (aceType instanceof AceEth) { + return true; // L2 only rules are possible for IP6 traffic + } + if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) { + return true; + } + return false; + } + + void write(@Nonnull final InstanceIdentifier id, final int swIfIndex, final InterfaceMode mode, + @Nonnull final List acls, final AccessLists.DefaultAction defaultAction, + @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags) + throws WriteFailedException { + checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range"); + + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 1; + request.swIfIndex = swIfIndex; + request.l2TableIndex = NOT_DEFINED; + request.ip4TableIndex = NOT_DEFINED; + request.ip6TableIndex = NOT_DEFINED; + + if (InterfaceMode.L2.equals(mode)) { + final List aces = getACEs(acls, writeContext, ace -> true); + request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags); + } else { + final List ip4Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp4Path)); + request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags); + final List ip6Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp6Path)); + request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags); + } + + final CompletionStage inputAclSetInterfaceReplyCompletionStage = + jvpp.inputAclSetInterface(request); + getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + } + + private static List getACEs(@Nonnull final List acls, @Nonnull final WriteContext writeContext, + final Predicate filter) { + return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter) + .collect(Collectors.toList()); + } + + private int writeAces(final InstanceIdentifier id, final List aces, + final AccessLists.DefaultAction defaultAction, final InterfaceMode mode, + final int vlanTags) throws WriteFailedException { + if (aces.isEmpty()) { + return NOT_DEFINED; + } + + int nextTableIndex = configureDefaultAction(id, defaultAction); + final ListIterator iterator = aces.listIterator(aces.size()); + while (iterator.hasPrevious()) { + final Ace ace = iterator.previous(); + LOG.trace("Processing ACE: {}", ace); + + final AceWriter aceWriter = + aceWriters.get(AclType.fromAce(ace)); + if (aceWriter == null) { + LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass()); + } else { + final AceType aceType = ace.getMatches().getAceType(); + final PacketHandling action = ace.getActions().getPacketHandling(); + final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags); + nextTableIndex = createClassifyTable(id, ctRequest); + final ClassifyAddDelSession csRequest = + aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags); + createClassifySession(id, csRequest); + } + } + return nextTableIndex; + } + + private int configureDefaultAction(@Nonnull final InstanceIdentifier id, + final AccessLists.DefaultAction defaultAction) + throws WriteFailedException { + ClassifyAddDelTable ctRequest = createTable(-1); + if (AccessLists.DefaultAction.Permit.equals(defaultAction)) { + ctRequest.missNextIndex = -1; + } else { + ctRequest.missNextIndex = 0; + } + ctRequest.mask = new byte[16]; + ctRequest.skipNVectors = 0; + ctRequest.matchNVectors = 1; + return createClassifyTable(id, ctRequest); + } + + private int createClassifyTable(@Nonnull final InstanceIdentifier id, + @Nonnull final ClassifyAddDelTable request) + throws WriteFailedException { + final CompletionStage cs = jvpp.classifyAddDelTable(request); + + final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id); + return reply.newTableIndex; + } + + private void createClassifySession(@Nonnull final InstanceIdentifier id, + @Nonnull final ClassifyAddDelSession request) + throws WriteFailedException { + final CompletionStage cs = jvpp.classifyAddDelSession(request); + + getReplyForWrite(cs.toCompletableFuture(), id); + } + + private enum AclType { + ETH, IP4, IP6, ETH_AND_IP; + + @Nonnull + private static AclType fromAce(final Ace ace) { + AclType result = null; + final AceType aceType; + try { + aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceEth) { + result = ETH; + } else if (aceType instanceof AceIp) { + final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion(); + if (aceIpVersion instanceof AceIpv4) { + result = IP4; + } else { + result = IP6; + } + } else if (aceType instanceof AceIpAndEth) { + result = ETH_AND_IP; + } + } catch (NullPointerException e) { + throw new IllegalArgumentException("Incomplete ACE: " + ace, e); + } + if (result == null) { + throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType)); + } + return result; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java new file mode 100644 index 000000000..819561e78 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java @@ -0,0 +1,135 @@ +/* + * 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.ingress; + +import com.google.common.primitives.Ints; +import io.fd.honeycomb.translate.vpp.util.Ipv4Translator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +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.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv4HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; + +public interface Ip4AclTranslator extends Ipv4Translator { + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int DSCP_OFFSET = 15; + int DSCP_MASK = 0xfc; + + int IP_PROTOCOL_OFFSET = ETHER_TYPE_OFFSET + 11; + int IP_PROTOCOL_MASK = 0xff; + + int IP4_LEN = 4; + int IP4_MASK_BIT_LENGTH = 32; + int SRC_IP_OFFSET = ETHER_TYPE_OFFSET + 14; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN; + + default boolean ip4Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final ClassifyAddDelTable request, final Logger log) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip4.getSourceIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getSourceIpv4Network()), 0, request.mask, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + } + if (ip4.getDestinationIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getDestinationIpv4Network()), 0, request.mask, + baseOffset + DST_IP_OFFSET, IP4_LEN); + } + return aceIsEmpty; + } + + default boolean ip4Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final ClassifyAddDelSession request, final Logger log) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP4 etherType (0x0800) + request.match[baseOffset + ETHER_TYPE_OFFSET] = 0x08; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = 0x00; + } + if (header.getDscp() != null) { + noMatch = false; + request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (header.getDscp().getValue() << 2)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip4.getSourceIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getSourceIpv4Network()), 0, request.match, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + + } + if (ip4.getDestinationIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getDestinationIpv4Network()), 0, request.match, + baseOffset + DST_IP_OFFSET, IP4_LEN); + + } + return noMatch; + } + + class Impl { + private static byte[] toByteMask(final int prefixLength) { + final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength); + return Ints.toByteArray((int) mask); + } + + private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) { + final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) { + final String[] split = ipv4Prefix.getValue().split("/"); + final byte[] addressBytes = Ipv4Translator.INSTANCE.ipv4AddressNoZoneToArray(split[0]); + final byte[] mask = Impl.toByteMask(Byte.valueOf(split[1])); + for (int i = 0; i < addressBytes.length; ++i) { + addressBytes[i] &= mask[i]; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java new file mode 100644 index 000000000..eb2ec8c10 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java @@ -0,0 +1,169 @@ +/* + * 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.ingress; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.BitSet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv6HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; + +public interface Ip6AclTranslator { + + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET + 2; + int DSCP_MASK1 = 0x0f; + int DSCP_MASK2 = 0xc0; + int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET + 6; + int IP_PROTOCOL_MASK = 0xff; + int IP6_LEN = 16; + int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN; + + default boolean ip6Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final ClassifyAddDelTable request, final Logger log) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + // DCSP (bits 4-9 of IP6 header) + request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (ip6.getFlowLabel() != null) { + aceIsEmpty = false; + // bits 12-31 + 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 (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip6.getSourceIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getSourceIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length); + } + if (ip6.getDestinationIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getDestinationIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); + } + return aceIsEmpty; + } + + default boolean ip6Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final ClassifyAddDelSession request, final Logger log) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP6 etherType (0x86dd) + request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd; + } + if (header.getDscp() != null) { + noMatch = false; + final int dcsp = header.getDscp().getValue(); + // set bits 4-9 of IP6 header: + request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dcsp >> 2)); + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dcsp << 6)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (ip6.getFlowLabel() != null) { + noMatch = false; + final int flowLabel = ip6.getFlowLabel().getValue().intValue(); + // bits 12-31 + 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 (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip6.getSourceIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getSourceIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN); + } + if (ip6.getDestinationIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getDestinationIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); + } + return noMatch; + } + + class Impl { + private static final int IP6_MASK_BIT_LENGTH = 128; + + private static byte[] toByteMask(final int prefixLength) { + final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH); + mask.set(0, prefixLength, true); + if (prefixLength < IP6_MASK_BIT_LENGTH) { + mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false); + } + return mask.toByteArray(); + } + + private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) { + final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) { + final String[] split = ipv6Prefix.getValue().split("/"); + final byte[] addressBytes; + try { + addressBytes = InetAddress.getByName(split[0]).getAddress(); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Invalid IP6 address", e); + } + final byte[] mask = toByteMask(Short.valueOf(split[1])); + int pos = 0; + for (; pos < mask.length; ++pos) { + addressBytes[pos] &= mask[pos]; + } + // mask can be shorter that address, so we need to clear rest of the address: + for (; pos < addressBytes.length; ++pos) { + addressBytes[pos] = 0; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java new file mode 100644 index 000000000..a802ed3fa --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java @@ -0,0 +1,90 @@ +/* + * 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.ingress; + +import io.fd.honeycomb.translate.vpp.util.MacTranslator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; + +public interface L2AclTranslator extends MacTranslator { + + default boolean destinationMacAddressMask(final MacAddress dstMask, final MacAddress dstAddress, + final ClassifyAddDelTable request) { + // destination-mac-address or destination-mac-address-mask is present => + // ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00 + if (dstMask != null) { + final List parts = COLON_SPLITTER.splitToList(dstMask.getValue()); + int i = 0; + for (String part : parts) { + request.mask[i++] = parseHexByte(part); + } + return false; + } else if (dstAddress != null) { + for (int i = 0; i < 6; ++i) { + request.mask[i] = (byte) 0xff; + } + return false; + } + return true; + } + + default boolean sourceMacAddressMask(final MacAddress srcMask, final MacAddress srcAddress, + final ClassifyAddDelTable request) { + // source-mac-address or source-mac-address-mask => + // 00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00 + if (srcMask != null) { + final List parts = COLON_SPLITTER.splitToList(srcMask.getValue()); + int i = 6; + for (String part : parts) { + request.mask[i++] = parseHexByte(part); + } + return false; + } else if (srcAddress != null) { + for (int i = 6; i < 12; ++i) { + request.mask[i] = (byte) 0xff; + } + return false; + } + return true; + } + + default boolean destinationMacAddressMatch(final MacAddress dstAddress, final ClassifyAddDelSession request) { + if (dstAddress != null) { + final List parts = COLON_SPLITTER.splitToList(dstAddress.getValue()); + int i = 0; + for (String part : parts) { + request.match[i++] = parseHexByte(part); + } + return false; + } + return true; + } + + default boolean sourceMacAddressMatch(final MacAddress srcAddress, final ClassifyAddDelSession request) { + if (srcAddress != null) { + final List parts = COLON_SPLITTER.splitToList(srcAddress.getValue()); + int i = 6; + for (String part : parts) { + request.match[i++] = parseHexByte(part); + } + return false; + } + return true; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java index fe4e22142..fdefc0855 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java @@ -48,10 +48,10 @@ import org.slf4j.LoggerFactory; public class SubInterfaceIetfAclCustomizer implements WriterCustomizer { private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public SubInterfaceIetfAclCustomizer(@Nonnull final IetfAClWriter aclWriter, + public SubInterfaceIetfAclCustomizer(@Nonnull final IetfAclWriter aclWriter, @Nonnull final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java index 1a89a5d98..546de89d7 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java @@ -22,11 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; 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; 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.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; @@ -36,8 +33,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceEthWriterTest { - @Mock - private FutureJVppCore jvpp; private AceEthWriter writer; private PacketHandling action; private AceEth aceEth; @@ -45,7 +40,7 @@ public class AceEthWriterTest { @Before public void setUp() { initMocks(this); - writer = new AceEthWriter(jvpp); + writer = new AceEthWriter(); action = new DenyBuilder().setDeny(true).build(); aceEth = new AceEthBuilder() .setDestinationMacAddress(new MacAddress("11:22:33:44:55:66")) @@ -56,9 +51,9 @@ public class AceEthWriterTest { } @Test - public void testCreateClassifyTable() { + public void testCreateTable() { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceEth, InterfaceMode.L2, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceEth, InterfaceMode.L2, nextTableIndex, 0); assertEquals(1, request.isAdd); assertEquals(-1, request.tableIndex); @@ -78,15 +73,10 @@ public class AceEthWriterTest { assertArrayEquals(expectedMask, request.mask); } - @Test(expected = IllegalArgumentException.class) - public void testCreateClassifyTableForL3Interface() { - writer.createClassifyTable(aceEth, InterfaceMode.L3, 42, 0); - } - @Test public void testCreateClassifySession() { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceEth, InterfaceMode.L2, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceEth, InterfaceMode.L2, tableIndex, 0); assertEquals(1, request.isAdd); assertEquals(tableIndex, request.tableIndex); @@ -101,17 +91,4 @@ public class AceEthWriterTest { }; assertArrayEquals(expectedMatch, request.match); } - - @Test(expected = IllegalArgumentException.class) - public void testCreateClassifySessionForL3Interface() { - writer.createClassifySession(action, aceEth, InterfaceMode.L3, 42, 0); - } - - @Test - public void testSetClassifyTable() { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.l2TableIndex); - } } \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java index 1a7045529..258a937ac 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java @@ -16,17 +16,14 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclTranslator.VLAN_TAG_LEN; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; 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; 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.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; @@ -38,8 +35,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceIp4WriterTest { - @Mock - private FutureJVppCore jvpp; private AceIp4Writer writer; private PacketHandling action; private AceIp aceIp; @@ -47,7 +42,7 @@ public class AceIp4WriterTest { @Before public void setUp() throws Exception { initMocks(this); - writer = new AceIp4Writer(jvpp); + writer = new AceIp4Writer(); action = new DenyBuilder().setDeny(true).build(); aceIp = new AceIpBuilder() .setProtocol((short) 132) @@ -120,46 +115,46 @@ public class AceIp4WriterTest { } @Test - public void testCreateClassifyTable() throws Exception { + public void testCreateTable() throws Exception { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, false); } @Test - public void testCreateClassifyTableForL2Interface() throws Exception { + public void testCreateTableForL2Interface() throws Exception { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, true); } @Test - public void testCreateClassifyTable1VlanTag() throws Exception { + public void testCreateTable1VlanTag() throws Exception { final int nextTableIndex = 42; final int vlanTags = 1; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test - public void testCreateClassifyTable2VlanTags() throws Exception { + public void testCreateTable2VlanTags() throws Exception { final int nextTableIndex = 42; final int vlanTags = 2; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test public void testCreateClassifySession() throws Exception { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, false); } @Test public void testCreateClassifySessionForL2Interface() throws Exception { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L2, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L2, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, true); } @@ -167,7 +162,7 @@ public class AceIp4WriterTest { public void testCreateClassifySession1VlanTag() throws Exception { final int tableIndex = 123; final int vlanTags = 1; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } @@ -175,16 +170,8 @@ public class AceIp4WriterTest { public void testCreateClassifySession2VlanTags() throws Exception { final int tableIndex = 123; final int vlanTags = 2; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } - - @Test - public void testSetClassifyTable() throws Exception { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.ip4TableIndex); - } } \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java index 01eaa454d..08f90557e 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java @@ -16,17 +16,14 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclTranslator.VLAN_TAG_LEN; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; 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; 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.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; @@ -39,8 +36,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceIp6WriterTest { - @Mock - private FutureJVppCore jvpp; private AceIp6Writer writer; private PacketHandling action; private AceIp aceIp; @@ -48,7 +43,7 @@ public class AceIp6WriterTest { @Before public void setUp() { initMocks(this); - writer = new AceIp6Writer(jvpp); + writer = new AceIp6Writer(); action = new DenyBuilder().setDeny(true).build(); aceIp = new AceIpBuilder() .setProtocol((short) 132) @@ -128,36 +123,36 @@ public class AceIp6WriterTest { } @Test - public void testCreateClassifyTable() { + public void testCreateTable() { final int nextTableIndex = 42; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, false); } @Test - public void testCreateClassifyTableForL2Interface() { + public void testCreateTableForL2Interface() { final int nextTableIndex = 42; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); + writer.createTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, true); } @Test - public void testCreateClassifyTable1VlanTag() { + public void testCreateTable1VlanTag() { final int nextTableIndex = 42; final int vlanTags = 1; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test - public void testCreateClassifyTable2VlanTags() { + public void testCreateTable2VlanTags() { final int nextTableIndex = 42; final int vlanTags = 2; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @@ -165,7 +160,7 @@ public class AceIp6WriterTest { public void testCreateClassifySession() { final int tableIndex = 123; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, 0); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, false); } @@ -173,7 +168,7 @@ public class AceIp6WriterTest { public void testCreateClassifySessionForL2Interface() { final int tableIndex = 123; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L2, tableIndex, 0); + writer.createSession(action, aceIp, InterfaceMode.L2, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, true); } @@ -182,7 +177,7 @@ public class AceIp6WriterTest { final int tableIndex = 123; final int vlanTags = 1; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } @@ -191,15 +186,7 @@ public class AceIp6WriterTest { final int tableIndex = 123; final int vlanTags = 2; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } - - @Test - public void testSetClassifyTable() { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.ip6TableIndex); - } } \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java new file mode 100644 index 000000000..1da23072b --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java @@ -0,0 +1,114 @@ +/* + * 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.ingress; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import org.junit.Before; +import org.junit.Test; +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; +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.packet.handling.DenyBuilder; +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.yang.types.rev130715.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEthBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4Builder; + +public class AceIpAndEthWriterTest { + + private AceIpAndEthWriter writer; + private PacketHandling action; + private AceIpAndEth ace; + + @Before + public void setUp() { + initMocks(this); + writer = new AceIpAndEthWriter(); + action = new DenyBuilder().setDeny(true).build(); + ace = new AceIpAndEthBuilder() + .setDestinationMacAddress(new MacAddress("11:22:33:44:55:66")) + .setSourceMacAddress(new MacAddress("aa:bb:cc:dd:ee:ff")) + .setAceIpVersion(new AceIpv4Builder() + .setSourceIpv4Network(new Ipv4Prefix("1.2.3.4/32")).build()) + .build(); + } + + @Test + public void testCreateTable() { + final int nextTableIndex = 42; + final ClassifyAddDelTable request = writer.createTable(ace, InterfaceMode.L2, nextTableIndex, 0); + + assertEquals(1, request.isAdd); + assertEquals(-1, request.tableIndex); + assertEquals(1, request.nbuckets); + assertEquals(nextTableIndex, request.nextTableIndex); + assertEquals(0, request.skipNVectors); + assertEquals(3, request.matchNVectors); + assertEquals(AceEthWriter.TABLE_MEM_SIZE, request.memorySize); + + byte[] expectedMask = new byte[] { + // destination MAC: + -1, -1, -1, -1, -1, -1, + // source MAC: + -1, -1, -1, -1, -1, -1, + // ether type: + -1, -1, + // IP header + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // source address: + -1, -1, -1, -1, + // destination address: + 0, 0, 0, 0, + // padding: + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assertArrayEquals(expectedMask, request.mask); + } + + @Test + public void testCreateClassifySession() { + final int tableIndex = 123; + final ClassifyAddDelSession request = writer.createSession(action, ace, InterfaceMode.L2, tableIndex, 0); + + assertEquals(1, request.isAdd); + assertEquals(tableIndex, request.tableIndex); + assertEquals(0, request.hitNextIndex); + + byte[] expectedMatch = new byte[] { + // destination MAC: + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66, + // source MAC: + (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 0xee, (byte) 0xff, + // ether type (IP4): + 0x08, 0, + // IP header + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // source address: + 1, 2, 3, 4, + // destination address: + 0, 0, 0, 0, + // padding: + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assertArrayEquals(expectedMatch, request.match); + } +} \ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java index 9e010d0f8..6c7eea8b1 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java @@ -84,7 +84,7 @@ public class IetfAclCustomizerTest extends WriterCustomizerTest { @Override protected void setUp() { - customizer = new IetfAclCustomizer(new IetfAClWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); + customizer = new IetfAclCustomizer(new IetfAclWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); defineMapping(mappingContext, IF_NAME, IF_INDEX, IFC_TEST_INSTANCE); acl = new IngressBuilder().setAccessLists( new AccessListsBuilder().setAcl( diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java index 53b7ccf7e..76c681ca9 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.common.base.Optional; -import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteFailedException; import io.fd.honeycomb.vpp.test.write.WriterCustomizerTest; @@ -71,7 +70,7 @@ public class SubInterfaceIetfAclCustomizerTest extends WriterCustomizerTest { @Override protected void setUp() { customizer = - new SubInterfaceIetfAclCustomizer(new IetfAClWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); + new SubInterfaceIetfAclCustomizer(new IetfAclWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); defineMapping(mappingContext, IF_NAME, IF_INDEX, IFC_TEST_INSTANCE); acl = new IngressBuilder().setAccessLists( @@ -134,7 +133,7 @@ public class SubInterfaceIetfAclCustomizerTest extends WriterCustomizerTest { new SubInterfaceBuilder().build() )); - when(writeContext.readAfter(IetfAclWriter.ACL_ID.child( + when(writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child( org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, new AclKey(ACL_NAME, ACL_TYPE)))).thenReturn(Optional.of( new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclBuilder() -- cgit 1.2.3-korg