HONEYCOMB-233: add support for mixing L2/L3 rules 65/3365/5
authorMarek Gradzki <mgradzki@cisco.com>
Mon, 10 Oct 2016 12:55:15 +0000 (14:55 +0200)
committerMaros Marsalek <mmarsale@cisco.com>
Thu, 13 Oct 2016 11:27:29 +0000 (11:27 +0000)
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 <mgradzki@cisco.com>
29 files changed:
v3po/api/src/main/yang/v3po.yang
v3po/api/src/main/yang/vpp-acl.yang
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java [deleted file]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java [moved from v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java with 53% similarity]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java [new file with mode: 0644]
v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java [new file with mode: 0644]
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java
v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java
vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java
vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/MacTranslator.java

index b2d0a74..a5ccec5 100644 (file)
@@ -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" {
index 76e1eda..d0d24c9 100644 (file)
@@ -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;
index 8e001b7..301c305 100644 (file)
@@ -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<IetfAClWriter> {
+class IetfAClWriterProvider implements Provider<IetfAclWriter> {
 
     private final FutureJVppCore jvpp;
 
@@ -31,7 +31,7 @@ class IetfAClWriterProvider implements Provider<IetfAClWriter> {
     }
 
     @Override
-    public IetfAClWriter get() {
-        return new IetfAClWriter(jvpp);
+    public IetfAclWriter get() {
+        return new IetfAclWriter(jvpp);
     }
 }
index 9c46ba3..be3801b 100644 (file)
@@ -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,
index f4dc076..afa362d 100644 (file)
@@ -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;
index de29d5d..eaecdf0 100644 (file)
@@ -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());
 
index 0eff83b..3dc9e22 100644 (file)
@@ -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<Egress> {
     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");
     }
index 2a20a75..b519846 100644 (file)
@@ -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<Egress> {
     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 (file)
index a5bbab5..0000000
+++ /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. <p/> Creates one classify
- * table with single session per ACE.
- *
- * @param <T> type of access control list entry
- */
-abstract class AbstractAceWriter<T extends AceType> 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<Ace> 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<Ace> 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<ClassifyAddDelTableReply> 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<ClassifyAddDelSessionReply> 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;
-    }
-}
index 939e4eb..91ab927 100644 (file)
 
 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<AceEth> implements MacTranslator {
+final class AceEthWriter implements AceWriter<AceEth>, 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<String> 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<String> 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<AceEth> 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<String> 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<String> 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;
-    }
 }
index 2f8d030..affc873 100644 (file)
@@ -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<AceIp> implements Ipv4Translator {
+final class AceIp4Writer implements AceWriter<AceIp>, 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<AceIp> 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;
-    }
 }
index f1cccba..b118644 100644 (file)
@@ -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<AceIp> {
+final class AceIp6Writer implements AceWriter<AceIp>, 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<AceIp> {
     }
 
     @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<AceIp> {
         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 (file)
index 0000000..40a050a
--- /dev/null
@@ -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<AceIpAndEth>, 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;
+    }
+}
index 7ba44f1..790b6d6 100644 (file)
 
 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 <T> type of access control list entry
  */
-interface AceWriter {
+interface AceWriter<T extends AceType> {
+    /**
+     * @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<Ace> 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 (file)
index 0000000..9a931a9
--- /dev/null
@@ -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;
+    }
+}
index 9074684..4eac0fa 100644 (file)
@@ -41,10 +41,10 @@ import org.slf4j.LoggerFactory;
 public class IetfAclCustomizer implements WriterCustomizer<Ingress> {
 
     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");
@@ -20,10 +20,11 @@ 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.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;
@@ -33,8 +34,10 @@ 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;
@@ -44,30 +47,35 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont
 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.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+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 {
+public final class IetfAclWriter implements JvppReplyConsumer, AclTranslator {
 
-    private static final Logger LOG = LoggerFactory.getLogger(IetfAClWriter.class);
+    private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class);
+    private static final int NOT_DEFINED = -1;
     private final FutureJVppCore jvpp;
 
-    private Map<AclType, AceWriter> aceWriters = new HashMap<>();
+    private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>();
 
-    public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) {
+    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));
+        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<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
@@ -77,12 +85,12 @@ public final class IetfAClWriter implements JvppReplyConsumer {
 
         // ietf-acl updates are handled first, so we use writeContext.readAfter
         final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl>
-                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)));
+            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();
+            acl = aclOptional.get();
 
         final AccessListEntries accessListEntries = acl.getAccessListEntries();
         checkArgument(accessListEntries != null, "access list entries not configured");
@@ -91,7 +99,7 @@ public final class IetfAClWriter implements JvppReplyConsumer {
     }
 
     void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
-            throws WriteFailedException {
+        throws WriteFailedException {
         final ClassifyTableByInterface request = new ClassifyTableByInterface();
         request.swIfIndex = swIfIndex;
 
@@ -110,7 +118,7 @@ public final class IetfAClWriter implements JvppReplyConsumer {
 
     private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id,
                                         final ClassifyTableByInterfaceReply currentState)
-            throws WriteFailedException {
+        throws WriteFailedException {
         final InputAclSetInterface request = new InputAclSetInterface();
         request.isAdd = 0;
         request.swIfIndex = currentState.swIfIndex;
@@ -118,12 +126,12 @@ public final class IetfAClWriter implements JvppReplyConsumer {
         request.ip4TableIndex = currentState.ip4TableId;
         request.ip6TableIndex = currentState.ip6TableId;
         final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
-                jvpp.inputAclSetInterface(request);
+            jvpp.inputAclSetInterface(request);
         getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
     }
 
     private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
-            throws WriteFailedException {
+        throws WriteFailedException {
 
         if (tableIndex == -1) {
             return; // classify table id is absent
@@ -135,49 +143,138 @@ public final class IetfAClWriter implements JvppReplyConsumer {
     }
 
     void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls,
-               final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext)
-            throws WriteFailedException {
+               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<Acl> acls, final AccessLists.DefaultAction defaultAction,
                @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags)
-            throws WriteFailedException {
-
-        // filter ACE entries and group by AceType
-        final Map<AclType, List<Ace>> acesByType = acls.stream()
-                .flatMap(acl -> aclToAceStream(acl, writeContext))
-                .collect(Collectors.groupingBy(AclType::fromAce));
+        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 = -1;
-        request.ip4TableIndex = -1;
-        request.ip6TableIndex = -1;
+        request.l2TableIndex = NOT_DEFINED;
+        request.ip4TableIndex = NOT_DEFINED;
+        request.ip6TableIndex = NOT_DEFINED;
+
+        if (InterfaceMode.L2.equals(mode)) {
+            final List<Ace> aces = getACEs(acls, writeContext, ace -> true);
+            request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags);
+        } else {
+            final List<Ace> ip4Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp4Path));
+            request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags);
+            final List<Ace> ip6Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp6Path));
+            request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags);
+        }
+
+        final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
+            jvpp.inputAclSetInterface(request);
+        getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
+    }
 
-        // for each AceType:
-        for (Map.Entry<AclType, List<Ace>> entry : acesByType.entrySet()) {
-            final AclType aceType = entry.getKey();
-            final List<Ace> aces = entry.getValue();
-            LOG.trace("Processing ACEs of {} type: {}", aceType, aces);
+    private static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext,
+                                           final Predicate<? super Ace> filter) {
+        return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter)
+            .collect(Collectors.toList());
+    }
+
+    private int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces,
+                          final AccessLists.DefaultAction defaultAction, final InterfaceMode mode,
+                          final int vlanTags) throws WriteFailedException {
+        if (aces.isEmpty()) {
+            return NOT_DEFINED;
+        }
 
-            final AceWriter aceWriter = aceWriters.get(aceType);
+        int nextTableIndex = configureDefaultAction(id, defaultAction);
+        final ListIterator<Ace> 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.", aceType);
+                LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass());
             } else {
-                aceWriter.write(id, aces, mode, defaultAction, request, numberOfTags);
+                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;
+    }
 
-        final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
-                jvpp.inputAclSetInterface(request);
-        getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
+    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<ClassifyAddDelTableReply> 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<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request);
+
+        getReplyForWrite(cs.toCompletableFuture(), id);
     }
 
     private enum AclType {
-        ETH, IP4, IP6;
+        ETH, IP4, IP6, ETH_AND_IP;
 
         @Nonnull
         private static AclType fromAce(final Ace ace) {
@@ -194,6 +291,8 @@ public final class IetfAClWriter implements JvppReplyConsumer {
                     } else {
                         result = IP6;
                     }
+                } else if (aceType instanceof AceIpAndEth) {
+                    result = ETH_AND_IP;
                 }
             } catch (NullPointerException e) {
                 throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
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 (file)
index 0000000..819561e
--- /dev/null
@@ -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 (file)
index 0000000..eb2ec8c
--- /dev/null
@@ -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 (file)
index 0000000..a802ed3
--- /dev/null
@@ -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<String> 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<String> 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<String> 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<String> parts = COLON_SPLITTER.splitToList(srcAddress.getValue());
+            int i = 6;
+            for (String part : parts) {
+                request.match[i++] = parseHexByte(part);
+            }
+            return false;
+        }
+        return true;
+    }
+}
index fe4e221..fdefc08 100644 (file)
@@ -48,10 +48,10 @@ import org.slf4j.LoggerFactory;
 public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Ingress> {
 
     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");
index 1a89a5d..546de89 100644 (file)
@@ -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
index 1a70455..258a937 100644 (file)
 
 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
index 01eaa45..08f9055 100644 (file)
 
 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 (file)
index 0000000..1da2307
--- /dev/null
@@ -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
index 9e010d0..6c7eea8 100644 (file)
@@ -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(
index 53b7ccf..76c681c 100644 (file)
@@ -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()
index 84b0a2e..56aa4f1 100644 (file)
@@ -30,6 +30,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.
  */
 public interface Ipv4Translator extends ByteDataTranslator {
 
+    Ipv4Translator INSTANCE = new Ipv4Translator() {};
+
     /**
      * Creates address array from address part of {@link Ipv4Prefix}
      */
index a947f95..14cc289 100644 (file)
@@ -28,7 +28,7 @@ import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.binary.Hex;
 
 /**
- * Trait providing logic for translation of ipv4-related data
+ * Trait providing logic for translation of MAC address data
  */
 public interface MacTranslator {