summaryrefslogtreecommitdiffstats
path: root/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl
diff options
context:
space:
mode:
Diffstat (limited to 'vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl')
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/IetfAclWriter.java105
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AbstractIetfAclWriter.java252
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceEthWriter.java85
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp4Writer.java94
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp6Writer.java99
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIpAndEthWriter.java132
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceWriter.java54
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManager.java53
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManagerImpl.java68
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTranslator.java74
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/IetfAclWriter.java47
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip4AclTranslator.java149
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip6AclTranslator.java182
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/L2AclTranslator.java90
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/PortPair.java126
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/EgressIetfAclWriter.java120
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/IetfAclCustomizer.java85
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/SubInterfaceIetfAclCustomizer.java105
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclCustomizer.java83
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclWriter.java73
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IetfAclCustomizer.java89
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IngressIetfAclWriter.java118
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceAclCustomizer.java93
-rw-r--r--vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceIetfAclCustomizer.java108
24 files changed, 2484 insertions, 0 deletions
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/IetfAclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/IetfAclWriter.java
new file mode 100644
index 000000000..41ca796e7
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/IetfAclWriter.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl;
+
+import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.Optional;
+import java.util.stream.Stream;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl;
+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.interfaces.rev140508.Interfaces;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp._interface.acl.rev161214.VppInterfaceAclAugmentation;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Writer customizer responsible for Access Control Lists management. Does not send any messages to VPP. All the config
+ * data are stored in HC and used when acl is assigned/unassigned to/from an interface.
+ *
+ * ACLs that are currently assigned to an interface cannot be updated/deleted.
+ */
+public class IetfAclWriter implements ListWriterCustomizer<Acl, AclKey> {
+
+ public static final InstanceIdentifier<AccessLists> ACL_ID =
+ InstanceIdentifier.create(AccessLists.class);
+
+ private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class);
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ LOG.debug("Creating ACL: iid={} dataAfter={}", id, dataAfter);
+
+ // no vpp call, just updates DataTree
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore,
+ @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("Updating ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter);
+
+ if (isAssigned(dataAfter, writeContext)) {
+ throw new WriteFailedException(id,
+ String.format("Failed to update data at %s: acl %s is already assigned", id, dataAfter));
+ }
+
+ LOG.debug("Updating unassigned ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter);
+
+ // no vpp call, just updates DataTree
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ LOG.debug("Deleting ACL: iid={} dataBefore={}", id, dataBefore);
+
+ if (isAssigned(dataBefore, writeContext)) {
+ throw new WriteFailedException(id,
+ String.format("Failed to delete data at %s: acl %s is already assigned", id, dataBefore));
+ }
+
+ LOG.debug("Deleting unassigned ACL: iid={} dataBefore={}", id, dataBefore);
+
+ // no vpp call, just updates DataTree
+ }
+
+ private static boolean isAssigned(@Nonnull final Acl acl,
+ @Nonnull final WriteContext writeContext) {
+ final String aclName = acl.getAclName();
+ final Class<? extends AclBase> aclType = acl.getAclType();
+ final Interfaces interfaces = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class)).get();
+
+ return interfaces.getInterface().stream()
+ .map(i -> Optional.ofNullable(i.getAugmentation(VppInterfaceAclAugmentation.class))
+ .map(aug -> aug.getIetfAcl())
+ .map(ietfAcl -> ietfAcl.getIngress())
+ .map(ingress -> ingress.getAccessLists())
+ .map(accessLists -> accessLists.getAcl()))
+ .flatMap(iacl -> iacl.isPresent()
+ ? iacl.get().stream()
+ : Stream.empty())
+ .filter(assignedAcl -> aclName.equals(assignedAcl.getName()) && aclType.equals(assignedAcl.getType()))
+ .findFirst().isPresent();
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AbstractIetfAclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AbstractIetfAclWriter.java
new file mode 100644
index 000000000..59d314c03
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AbstractIetfAclWriter.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import io.fd.hc2vpp.common.translate.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.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.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.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.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.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 abstract class AbstractIetfAclWriter implements IetfAclWriter, JvppReplyConsumer, AclTranslator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractIetfAclWriter.class);
+ protected static final int NOT_DEFINED = -1;
+ protected final FutureJVppCore jvpp;
+
+ private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>();
+
+ public AbstractIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) {
+ this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null");
+ aceWriters.put(AclType.ETH, new AceEthWriter());
+ aceWriters.put(AclType.IP4, new AceIp4Writer());
+ aceWriters.put(AclType.IP6, new AceIp6Writer());
+ aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter());
+ }
+
+ private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl,
+ @Nonnull final WriteContext writeContext) {
+ final String aclName = assignedAcl.getName();
+ final Class<? extends AclBase> aclType = assignedAcl.getType();
+
+ // 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(io.fd.hc2vpp.vpp.classifier.write.acl.IetfAclWriter.ACL_ID.child(
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
+ new AclKey(aclName, aclType)));
+ checkArgument(aclOptional.isPresent(), "Acl lists not configured");
+ final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
+ acl = aclOptional.get();
+
+ final AccessListEntries accessListEntries = acl.getAccessListEntries();
+ checkArgument(accessListEntries != null, "access list entries not configured");
+
+ return accessListEntries.getAce().stream();
+ }
+
+ protected void removeClassifyTables(@Nonnull final InstanceIdentifier<?> id, @Nonnull final MappingEntry entry)
+ throws WriteFailedException {
+ removeClassifyTable(id, entry.getL2TableId());
+ removeClassifyTable(id, entry.getIp4TableId());
+ removeClassifyTable(id, entry.getIp6TableId());
+ }
+
+ private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex)
+ throws WriteFailedException {
+
+ if (tableIndex == -1) {
+ return; // classify table id is absent
+ }
+ final ClassifyAddDelTable request = new ClassifyAddDelTable();
+ request.delChain = 1;
+ request.tableIndex = tableIndex;
+ final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request);
+ getReplyForDelete(cs.toCompletableFuture(), id);
+ }
+
+ protected static boolean appliesToIp4Path(final Ace ace) {
+ final AceType aceType = ace.getMatches().getAceType();
+ final AclType aclType = AclType.fromAce(ace);
+ if (aclType == AclType.IP4) {
+ return true;
+ }
+ if (aclType == AclType.ETH) {
+ return true; // L2 only rules are possible for IP4 traffic
+ }
+ if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType).getAceIpAndEthNodes()
+ .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.and.eth.nodes.ace.ip.version.AceIpv4) {
+ return true;
+ }
+ return false;
+ }
+
+ protected static boolean appliesToIp6Path(final Ace ace) {
+ final AceType aceType = ace.getMatches().getAceType();
+ final AclType aclType = AclType.fromAce(ace);
+ if (aclType == AclType.IP6) {
+ return true;
+ }
+ if (aclType == AclType.ETH) {
+ return true; // L2 only rules are possible for IP6 traffic
+ }
+ if (aclType == AclType.ETH_AND_IP && ((AceIpAndEth) aceType).getAceIpAndEthNodes()
+ .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.and.eth.nodes.ace.ip.version.AceIpv6) {
+ return true;
+ }
+ return false;
+ }
+
+ protected 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());
+ }
+
+ protected 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;
+ }
+
+ 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.", ace.getClass());
+ } else {
+ final AceType aceType = ace.getMatches().getAceType();
+ final PacketHandling action = ace.getActions().getPacketHandling();
+ final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags);
+ nextTableIndex = createClassifyTable(id, ctRequest);
+ final List<ClassifyAddDelSession> sessionRequests =
+ aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags);
+ for (ClassifyAddDelSession csRequest : sessionRequests) {
+ createClassifySession(id, csRequest);
+ }
+ }
+ }
+ return nextTableIndex;
+ }
+
+ private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id,
+ final AccessLists.DefaultAction defaultAction)
+ throws WriteFailedException {
+ ClassifyAddDelTable ctRequest = createTable(-1);
+ if (AccessLists.DefaultAction.Permit.equals(defaultAction)) {
+ ctRequest.missNextIndex = -1;
+ } else {
+ ctRequest.missNextIndex = 0;
+ }
+ ctRequest.mask = new byte[16];
+ ctRequest.skipNVectors = 0;
+ ctRequest.matchNVectors = 1;
+ return createClassifyTable(id, ctRequest);
+ }
+
+ private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id,
+ @Nonnull final ClassifyAddDelTable request)
+ throws WriteFailedException {
+ final CompletionStage<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_AND_IP;
+
+ @Nonnull
+ private static AclType fromAce(final Ace ace) {
+ AclType result = null;
+ final AceType aceType;
+ try {
+ aceType = ace.getMatches().getAceType();
+ if (aceType instanceof AceEth) {
+ result = ETH;
+ } else if (aceType instanceof AceIp) {
+ final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion();
+ if (aceIpVersion == null) {
+ throw new IllegalArgumentException("Incomplete ACE (ip-version was not provided): " + ace);
+ }
+ if (aceIpVersion instanceof AceIpv4) {
+ result = IP4;
+ } else if (aceIpVersion instanceof AceIpv6) {
+ result = IP6;
+ }
+ } else if (aceType instanceof AceIpAndEth) {
+ result = ETH_AND_IP;
+ }
+ } catch (NullPointerException e) {
+ throw new IllegalArgumentException("Incomplete ACE: " + ace, e);
+ }
+ if (result == null) {
+ throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType));
+ }
+ return result;
+ }
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceEthWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceEthWriter.java
new file mode 100644
index 000000000..2a88239cc
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceEthWriter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+import java.util.Collections;
+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;
+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.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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);
+
+ @Override
+ 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 =
+ 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()));
+ }
+
+ request.skipNVectors = 0;
+ request.matchNVectors = MATCH_N_VECTORS;
+
+ LOG.debug("ACE rule={} translated to table={}.", aceEth, request);
+ return request;
+ }
+
+ @Override
+ public List<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 = 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()));
+ }
+
+ LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceEth, request);
+ return Collections.singletonList(request);
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp4Writer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp4Writer.java
new file mode 100644
index 000000000..0ae1315a8
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp4Writer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+import java.util.ArrayList;
+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;
+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.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class AceIp4Writer implements AceWriter<AceIp>, AclTranslator, Ip4AclTranslator {
+
+ @VisibleForTesting
+ static final int MATCH_N_VECTORS = 3; // number of 16B vectors
+ private static final int TABLE_MASK_LENGTH = 48;
+ private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class);
+
+ @Override
+ 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 int numberOfSessions = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()).size();
+ final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions);
+ request.skipNVectors = 0; // match entire L2 and L3 header
+ request.matchNVectors = MATCH_N_VECTORS;
+ request.mask = new byte[TABLE_MASK_LENGTH];
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ boolean aceIsEmpty = ip4Mask(baseOffset, mode, aceIp, ipVersion, request);
+ if (aceIsEmpty) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define packet field match values", aceIp.toString()));
+ }
+
+ LOG.debug("ACE rule={} translated to table={}.", aceIp, request);
+ return request;
+ }
+
+ @Override
+ public List<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 List<PortPair> portPairs = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange());
+ final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size());
+ for (final PortPair pair : portPairs) {
+ final ClassifyAddDelSession request = createSession(action, tableIndex);
+ request.match = new byte[TABLE_MASK_LENGTH];
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ boolean noMatch = ip4Match(baseOffset, mode, aceIp, ipVersion, pair.getSrc(), pair.getDst(), request);
+ if (noMatch) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define packet field match values", aceIp.toString()));
+ }
+
+ LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request);
+ requests.add(request);
+ }
+ return requests;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp6Writer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp6Writer.java
new file mode 100644
index 000000000..2004d4e06
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIp6Writer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+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 java.util.ArrayList;
+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;
+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.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+final class AceIp6Writer implements AceWriter<AceIp>, AclTranslator, Ip6AclTranslator {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class);
+
+ @Override
+ 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 int numberOfSessions = PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange()).size();
+ final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions);
+ request.skipNVectors = 0; // match entire L2 and L3 header
+ request.mask = new byte[getTableMaskLength(vlanTags)];
+ request.matchNVectors = request.mask.length/16;
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ boolean aceIsEmpty = ip6Mask(baseOffset, mode, aceIp, ipVersion, request);
+ if (aceIsEmpty) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define packet field match values", aceIp.toString()));
+ }
+
+ LOG.debug("ACE rule={} translated to table={}.", aceIp, request);
+ return request;
+ }
+
+ private static int getTableMaskLength(final int vlanTags) {
+ if (vlanTags == 2) {
+ return 80;
+ } else {
+ return 64;
+ }
+ }
+
+ @Override
+ public List<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 List<PortPair> portPairs =
+ PortPair.fromRange(aceIp.getSourcePortRange(), aceIp.getDestinationPortRange());
+
+ final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size());
+ for (final PortPair pair : portPairs) {
+ final ClassifyAddDelSession request = createSession(action, tableIndex);
+ request.match = new byte[getTableMaskLength(vlanTags)];
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ boolean noMatch = ip6Match(baseOffset, mode, aceIp, ipVersion, pair.getSrc(), pair.getDst(), request);
+ if (noMatch) {
+ throw new IllegalArgumentException(
+ String.format("Ace %s does not define packet field match values", aceIp.toString()));
+ }
+
+ LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request);
+ requests.add(request);
+ }
+ return requests;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIpAndEthWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIpAndEthWriter.java
new file mode 100644
index 000000000..8efe3564e
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceIpAndEthWriter.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+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 java.util.ArrayList;
+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;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.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.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.AceIpAndEthNodes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.and.eth.nodes.AceIpVersion;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.and.eth.nodes.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.and.eth.nodes.ace.ip.version.AceIpv6;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public 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, final int vlanTags) {
+ if (ace.getAceIpAndEthNodes().getAceIpVersion() != null) {
+ if (ace.getAceIpAndEthNodes().getAceIpVersion() instanceof AceIpv4) {
+ return 48;
+ } else {
+ return vlanTags == 2
+ ? 80
+ : 64;
+ }
+ }
+ return 16;
+ }
+
+ @Override
+ public ClassifyAddDelTable createTable(@Nonnull final AceIpAndEth ace, @Nullable final InterfaceMode mode,
+ final int nextTableIndex, final int vlanTags) {
+ final AceIpAndEthNodes nodes = ace.getAceIpAndEthNodes();
+ final int numberOfSessions = PortPair.fromRange(nodes.getSourcePortRange(), nodes.getDestinationPortRange()).size();
+ final ClassifyAddDelTable request = createTable(nextTableIndex, numberOfSessions);
+ final int maskLength = maskLength(ace, vlanTags);
+ request.mask = new byte[maskLength];
+ request.skipNVectors = 0;
+ request.matchNVectors = maskLength / 16;
+
+ boolean aceIsEmpty =
+ destinationMacAddressMask(nodes.getDestinationMacAddressMask(), nodes.getDestinationMacAddress(), request);
+ aceIsEmpty &= sourceMacAddressMask(nodes.getSourceMacAddressMask(), nodes.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 = nodes.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, nodes, ipVersion, request);
+ } else if (aceIpVersion instanceof AceIpv6) {
+ final AceIpv6 ipVersion = (AceIpv6) aceIpVersion;
+ aceIsEmpty &= ip6Mask(baseOffset, mode, nodes, ipVersion, request);
+ } 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 List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action,
+ @Nonnull final AceIpAndEth ace,
+ @Nullable final InterfaceMode mode, final int tableIndex,
+ final int vlanTags) {
+ final AceIpAndEthNodes nodes = ace.getAceIpAndEthNodes();
+ final List<PortPair> portPairs = PortPair.fromRange(nodes.getSourcePortRange(), nodes.getDestinationPortRange());
+ final List<ClassifyAddDelSession> requests = new ArrayList<>(portPairs.size());
+ for (final PortPair pair : portPairs) {
+ final ClassifyAddDelSession request = createSession(action, tableIndex);
+ request.match = new byte[maskLength(ace, vlanTags)];
+
+ boolean noMatch = destinationMacAddressMatch(nodes.getDestinationMacAddress(), request);
+ noMatch &= sourceMacAddressMatch(nodes.getSourceMacAddress(), request);
+
+ final AceIpVersion aceIpVersion = nodes.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, nodes, ipVersion, pair.getSrc(), pair.getDst(), request);
+ } else if (aceIpVersion instanceof AceIpv6) {
+ final AceIpv6 ipVersion = (AceIpv6) aceIpVersion;
+ noMatch &= ip6Match(baseOffset, mode, nodes, ipVersion, pair.getSrc(), pair.getDst(), request);
+ } 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);
+ requests.add(request);
+ }
+ return requests;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceWriter.java
new file mode 100644
index 000000000..228527f69
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AceWriter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession;
+import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable;
+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;
+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.classifier.acl.rev161214.InterfaceMode;
+
+/**
+ * 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<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);
+
+ /**
+ * @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
+ */
+ @Nonnull
+ List<ClassifyAddDelSession> createSession(@Nonnull final PacketHandling action, @Nonnull T ace,
+ @Nullable final InterfaceMode mode, final int tableIndex, final int vlanTags);
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManager.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManager.java
new file mode 100644
index 000000000..ed7960dd9
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import com.google.common.base.Optional;
+import io.fd.honeycomb.translate.MappingContext;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
+
+/**
+ * Manages interface metadata for ietf-acl model.
+ */
+public interface AclTableContextManager {
+
+ /**
+ * Obtains mapping entry for given interface.
+ *
+ * @param index interface index
+ * @param mappingContext mapping context providing context data for current transaction
+ * @return ietf-acl metadata for given interface
+ */
+ Optional<MappingEntry> getEntry(final int index, @Nonnull final MappingContext mappingContext);
+
+ /**
+ * Adds mapping entry.
+ *
+ * @param entry to be added
+ * @param mappingContext mapping context providing context data for current transaction
+ */
+ void addEntry(@Nonnull final MappingEntry entry, @Nonnull final MappingContext mappingContext);
+
+ /**
+ * Removes entry for given interface (if present).
+ *
+ * @param index interface index
+ * @param mappingContext mapping context providing context data for current transaction
+ */
+ void removeEntry(final int index, @Nonnull final MappingContext mappingContext);
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManagerImpl.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManagerImpl.java
new file mode 100644
index 000000000..16848acfd
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTableContextManagerImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import io.fd.honeycomb.translate.MappingContext;
+import javax.annotation.Nonnull;
+import javax.annotation.concurrent.ThreadSafe;
+import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.AclMappingEntryCtxAugmentation;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.AclMappingEntryContext;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.MappingTableKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+@ThreadSafe
+public class AclTableContextManagerImpl implements AclTableContextManager {
+
+ private MappingTable.Direction direction;
+
+ public AclTableContextManagerImpl(@Nonnull final MappingTable.Direction direction) {
+ this.direction = checkNotNull(direction, "direction should not be null");
+ }
+
+ @Nonnull
+ @Override
+ public synchronized Optional<MappingEntry> getEntry(final int swIfIndex, @Nonnull final MappingContext mappingContext) {
+ return mappingContext.read(getId(swIfIndex));
+ }
+
+ @Override
+ public synchronized void addEntry(@Nonnull final MappingEntry entry, @Nonnull final MappingContext mappingContext) {
+ mappingContext.put(getId(entry.getIndex()), entry);
+ }
+
+ @Override
+ public synchronized void removeEntry(final int swIfIndex, @Nonnull final MappingContext mappingContext) {
+ mappingContext.delete(getId(swIfIndex));
+ }
+
+ @VisibleForTesting
+ protected InstanceIdentifier<MappingEntry> getId(final int index) {
+ return InstanceIdentifier.create(Contexts.class)
+ .augmentation(AclMappingEntryCtxAugmentation.class)
+ .child(AclMappingEntryContext.class)
+ .child(MappingTable.class, new MappingTableKey(direction))
+ .child(MappingEntry.class, new MappingEntryKey(index));
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTranslator.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTranslator.java
new file mode 100644
index 000000000..e75d0af24
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/AclTranslator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+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.Nonnegative;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.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 {
+ int TABLE_MEM_SIZE = 8 * 1024;
+ int VLAN_TAG_LEN = 4;
+
+ default ClassifyAddDelTable createTable(final int nextTableIndex) {
+ return createTable(nextTableIndex, 1);
+ }
+
+ default ClassifyAddDelTable createTable(final int nextTableIndex, @Nonnegative final int numberOfSessions) {
+ final ClassifyAddDelTable request = new ClassifyAddDelTable();
+ request.isAdd = 1;
+ request.tableIndex = -1; // value not present
+ request.nbuckets = numberOfSessions;
+ request.nextTableIndex = nextTableIndex;
+
+
+ // 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 +1k*numberOfSessions for now
+ checkArgument(numberOfSessions>0, "negative numberOfSessions %s", numberOfSessions);
+ request.memorySize = TABLE_MEM_SIZE+1024*(numberOfSessions-1);
+ request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain
+ return request;
+ }
+
+ default ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, final int tableIndex) {
+ final ClassifyAddDelSession request = new ClassifyAddDelSession();
+ request.isAdd = 1;
+ request.tableIndex = tableIndex;
+ request.opaqueIndex = ~0; // value not used
+
+ if (action instanceof Permit) {
+ request.hitNextIndex = -1;
+ } // deny (0) is default value
+
+ return request;
+ }
+
+ default int getVlanTagsLen(final int vlanTags) {
+ return vlanTags * VLAN_TAG_LEN;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/IetfAclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/IetfAclWriter.java
new file mode 100644
index 000000000..7f31dc06d
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/IetfAclWriter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public interface IetfAclWriter {
+ default void write(@Nonnull final InstanceIdentifier<?> id, final int ifIndex, @Nonnull final List<Acl> acls,
+ final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode,
+ @Nonnull final WriteContext writeContext, @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException {
+ write(id, ifIndex, acls, defaultAction, mode, writeContext, 0, mappingContext);
+ }
+
+ void write(@Nonnull final InstanceIdentifier<?> id, int ifIndex, @Nonnull final List<Acl> acls,
+ final AccessLists.DefaultAction defaultAction, @Nullable InterfaceMode mode,
+ @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags,
+ @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException;
+
+ void deleteAcl(@Nonnull final InstanceIdentifier<?> id, int ifIndex, @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException;
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip4AclTranslator.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip4AclTranslator.java
new file mode 100644
index 000000000..ad70fd844
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip4AclTranslator.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import com.google.common.primitives.Ints;
+import io.fd.hc2vpp.common.translate.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.classifier.acl.rev161214.InterfaceMode;
+
+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;
+ int SRC_PORT_OFFSET = DST_IP_OFFSET + IP4_LEN;
+ int DST_PORT_OFFSET = SRC_PORT_OFFSET + 2;
+
+ default boolean ip4Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header,
+ final AclIpv4HeaderFields ip4, final ClassifyAddDelTable request) {
+ 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) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ aceIsEmpty = false;
+ request.mask[baseOffset + SRC_PORT_OFFSET] = (byte) 0xff;
+ request.mask[baseOffset + SRC_PORT_OFFSET + 1] = (byte) 0xff;
+ }
+ if (header.getDestinationPortRange() != null) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ aceIsEmpty = false;
+ request.mask[baseOffset + DST_PORT_OFFSET] = (byte) 0xff;
+ request.mask[baseOffset + DST_PORT_OFFSET + 1] = (byte) 0xff;
+ }
+ 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 Integer srcPort,
+ final Integer dstPort, final ClassifyAddDelSession request) {
+ 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 (srcPort != null) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ noMatch = false;
+ request.match[baseOffset + SRC_PORT_OFFSET] = (byte) (0xff & srcPort >> 8);
+ request.match[baseOffset + SRC_PORT_OFFSET + 1] = (byte) (0xff & srcPort);
+ }
+ if (header.getDestinationPortRange() != null) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ noMatch = false;
+ request.match[baseOffset + DST_PORT_OFFSET] = (byte) (0xff & dstPort >> 8);
+ request.match[baseOffset + DST_PORT_OFFSET + 1] = (byte) (0xff & dstPort);
+ }
+ 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/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip6AclTranslator.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip6AclTranslator.java
new file mode 100644
index 000000000..f11a3d6a3
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/Ip6AclTranslator.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+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.classifier.acl.rev161214.InterfaceMode;
+
+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;
+ int SRC_PORT_OFFSET = DST_IP_OFFSET + IP6_LEN;
+ int DST_PORT_OFFSET = SRC_PORT_OFFSET + 2;
+
+ default boolean ip6Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header,
+ final AclIpv6HeaderFields ip6, final ClassifyAddDelTable request) {
+ 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) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ aceIsEmpty = false;
+ request.mask[baseOffset + SRC_PORT_OFFSET] = (byte) 0xff;
+ request.mask[baseOffset + SRC_PORT_OFFSET + 1] = (byte) 0xff;
+ }
+ if (header.getDestinationPortRange() != null) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ aceIsEmpty = false;
+ request.mask[baseOffset + DST_PORT_OFFSET] = (byte) 0xff;
+ request.mask[baseOffset + DST_PORT_OFFSET + 1] = (byte) 0xff;
+ }
+ 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 Integer srcPort, final Integer dstPort, final ClassifyAddDelSession request) {
+ 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) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ noMatch = false;
+ request.match[baseOffset + SRC_PORT_OFFSET] = (byte) (0xff & srcPort >> 8);
+ request.match[baseOffset + SRC_PORT_OFFSET + 1] = (byte) (0xff & srcPort);
+ }
+ if (header.getDestinationPortRange() != null) {
+ // TODO (HONEYCOMB-253): port matching will not work correctly if Options are present
+ noMatch = false;
+ request.match[baseOffset + DST_PORT_OFFSET] = (byte) (0xff & dstPort >> 8);
+ request.match[baseOffset + DST_PORT_OFFSET + 1] = (byte) (0xff & dstPort);
+ }
+ 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/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/L2AclTranslator.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/L2AclTranslator.java
new file mode 100644
index 000000000..7addf28da
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/L2AclTranslator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import io.fd.hc2vpp.common.translate.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;
+
+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;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/PortPair.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/PortPair.java
new file mode 100644
index 000000000..339102117
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/common/PortPair.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.common;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.BiFunction;
+import javax.annotation.Nullable;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRange;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRange;
+
+/**
+ * Utility that produces cartesian product out of src and dst port ranges (used to translate ranges into
+ * list of classify sessions).
+ */
+final class PortPair {
+ private final Integer src;
+ private final Integer dst;
+
+ PortPair(@Nullable final Integer src, @Nullable final Integer dst) {
+ this.src = src;
+ this.dst = dst;
+ }
+
+ Integer getSrc() {
+ return src;
+ }
+
+ Integer getDst() {
+ return dst;
+ }
+
+ @Override
+ public String toString() {
+ return "(" + src + "," + dst + ")";
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final PortPair that = (PortPair) o;
+ if (!Objects.equals(src, that.src)) {
+ return false;
+ }
+ if (!Objects.equals(dst, that.dst)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(src, dst);
+ }
+
+ static List<PortPair> fromRange(final SourcePortRange srcRange,
+ final DestinationPortRange dstRange) {
+ final List<PortPair> result = new ArrayList<>();
+ if (srcRange == null && dstRange == null) {
+ result.add(new PortPair(null, null));
+ } else if (srcRange != null && dstRange == null) {
+ processSingleRange(result, srcRange.getLowerPort(), srcRange.getUpperPort(), PortPair::new);
+ } else if (srcRange == null && dstRange != null) {
+ processSingleRange(result, dstRange.getLowerPort(), dstRange.getUpperPort(),
+ (dst, src) -> new PortPair(src, dst));
+ } else {
+ processDoubleRange(result, srcRange, dstRange);
+ }
+ return result;
+ }
+
+ private static void processSingleRange(final List<PortPair> result,
+ final PortNumber lowerPort,
+ final PortNumber upperPort,
+ final BiFunction<Integer, Integer, PortPair> f) {
+ int low = lowerPort.getValue(); // mandatory
+ int hi = low;
+ if (upperPort != null) {
+ hi = upperPort.getValue();
+ }
+ for (; low <= hi; ++low) {
+ result.add(f.apply(low, null));
+ }
+ }
+
+ private static void processDoubleRange(final List<PortPair> result, final SourcePortRange srcRange,
+ final DestinationPortRange dstRange) {
+ int srcL = srcRange.getLowerPort().getValue();
+ int srcH = srcL;
+ if (srcRange.getUpperPort() != null) {
+ srcH = srcRange.getUpperPort().getValue();
+ }
+ int dstL = dstRange.getLowerPort().getValue();
+ int dstH = dstL;
+ if (dstRange.getUpperPort() != null) {
+ dstH = dstRange.getUpperPort().getValue();
+ }
+ for (int i=srcL; i <= srcH; ++i) {
+ for (int j=dstL; j <= dstH; ++j) {
+ result.add(new PortPair(i, j));
+ }
+ }
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/EgressIetfAclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/EgressIetfAclWriter.java
new file mode 100644
index 000000000..c279722af
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/EgressIetfAclWriter.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.egress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Optional;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.AbstractIetfAclWriter;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.AclTableContextManager;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.ClassifySetInterfaceL2Tables;
+import io.fd.vpp.jvpp.core.dto.ClassifySetInterfaceL2TablesReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.util.List;
+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.Matches;
+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.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class EgressIetfAclWriter extends AbstractIetfAclWriter {
+ private final AclTableContextManager aclCtx;
+
+ public EgressIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull AclTableContextManager aclCtx) {
+ super(futureJVppCore);
+ this.aclCtx = checkNotNull(aclCtx, "aclCtx should not be null");
+ }
+
+ @Override
+ public void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex,
+ @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException {
+ Optional<MappingEntry> optional = aclCtx.getEntry(swIfIndex, mappingContext);
+ checkState(optional.isPresent(), "Removing ACL id=%s, but acl mapping entry is not present", id);
+ final MappingEntry entry = optional.get();
+ unassignClassifyTables(id, swIfIndex);
+ removeClassifyTables(id, entry);
+ aclCtx.removeEntry(swIfIndex, mappingContext);
+ }
+
+ private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex)
+ throws WriteFailedException {
+ final ClassifySetInterfaceL2Tables request = new ClassifySetInterfaceL2Tables();
+ request.swIfIndex = swIfIndex;
+ request.ip4TableIndex = NOT_DEFINED;
+ request.ip6TableIndex = NOT_DEFINED;
+ request.otherTableIndex = NOT_DEFINED;
+ request.isInput = 0; // egress
+ final CompletionStage<ClassifySetInterfaceL2TablesReply> cs = jvpp.classifySetInterfaceL2Tables(request);
+ getReplyForDelete(cs.toCompletableFuture(), id);
+ }
+
+ @Override
+ public void write(@Nonnull final InstanceIdentifier<?> id, int swIfIndex, @Nonnull final List<Acl> acls,
+ @Nonnull final AccessLists.DefaultAction defaultAction, @Nullable InterfaceMode mode,
+ @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags,
+ @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException {
+ checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range");
+ checkArgument(InterfaceMode.L2.equals(mode), "Writing egress Acls is supported only in L2 mode");
+
+ final ClassifySetInterfaceL2Tables request = new ClassifySetInterfaceL2Tables();
+ request.isInput = 0; // egress
+ request.swIfIndex = swIfIndex;
+
+ // applied to packets according to their ether type
+ final List<Ace> ip4Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp4Path));
+ request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags);
+ final List<Ace> ip6Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp6Path));
+ request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags);
+ final List<Ace> aces = getACEs(acls, writeContext, EgressIetfAclWriter::isNotIpRule);
+ request.otherTableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags);
+
+ final MappingEntry entry = new MappingEntryBuilder().setIndex(swIfIndex)
+ .setIp4TableId(request.ip4TableIndex)
+ .setIp6TableId(request.ip6TableIndex)
+ .setL2TableId(request.otherTableIndex)
+ .build();
+ aclCtx.addEntry(entry, mappingContext);
+
+ try {
+ getReplyForWrite(jvpp.classifySetInterfaceL2Tables(request).toCompletableFuture(), id);
+ } catch (WriteFailedException e) {
+ removeClassifyTables(id, entry);
+ throw e;
+ }
+ }
+
+ private static boolean isNotIpRule(final Ace ace) {
+ final Matches matches = ace.getMatches();
+ checkArgument(matches != null, "Incomplete ACE: %s", ace);
+ return matches.getAceType() instanceof AceEth;
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/IetfAclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/IetfAclCustomizer.java
new file mode 100644
index 000000000..1d457ac52
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/IetfAclCustomizer.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.egress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.IetfAclWriter;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.ietf.acl.Egress;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+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 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");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String ifName = id.firstKeyOf(Interface.class).getName();
+ final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext());
+ LOG.debug("Adding egress ACLs for interface={}(id={}): {}", ifName, ifIndex, dataAfter);
+
+ final AccessLists accessLists = dataAfter.getAccessLists();
+ checkArgument(accessLists != null && accessLists.getAcl() != null,
+ "ietf-acl container does not define acl list");
+
+ if (!InterfaceMode.L2.equals(accessLists.getMode())) {
+ LOG.debug("Writing egress Acls is supported only in L2 mode. Ignoring config: {}", dataAfter);
+ return;
+ }
+
+ aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(),
+ writeContext, writeContext.getMappingContext());
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore,
+ @Nonnull final Egress dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("ACLs update: removing previously configured ACLs");
+ deleteCurrentAttributes(id, dataBefore, writeContext);
+ LOG.debug("ACLs update: adding updated ACLs");
+ writeCurrentAttributes(id, dataAfter, writeContext);
+ LOG.debug("ACLs update was successful");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String ifName = id.firstKeyOf(Interface.class).getName();
+ final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext());
+ LOG.debug("Removing ACLs for interface={}(id={}): {}", ifName, ifIndex, dataBefore);
+ aclWriter.deleteAcl(id, ifIndex, writeContext.getMappingContext());
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/SubInterfaceIetfAclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/SubInterfaceIetfAclCustomizer.java
new file mode 100644
index 000000000..9873a3143
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/egress/SubInterfaceIetfAclCustomizer.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.egress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getNumberOfTags;
+
+import com.google.common.base.Optional;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.v3po.util.SubInterfaceUtils;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.IetfAclWriter;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.ietf.acl.Egress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+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 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");
+ }
+
+ private String getSubInterfaceName(@Nonnull final InstanceIdentifier<Egress> id) {
+ final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class);
+ final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class);
+ return SubInterfaceUtils
+ .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue());
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String subInterfaceName = getSubInterfaceName(id);
+ final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext());
+ LOG.debug("Adding IETF-ACL for sub-interface: {}(id={}): {}", subInterfaceName, subInterfaceIndex, dataAfter);
+
+ final AccessLists accessLists = dataAfter.getAccessLists();
+ checkArgument(accessLists != null && accessLists.getAcl() != null,
+ "ietf-acl container does not define acl list");
+
+ final Optional<SubInterface> subInterfaceOptional =
+ writeContext.readAfter(id.firstIdentifierOf(SubInterface.class));
+ checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id);
+ final SubInterface subInterface = subInterfaceOptional.get();
+
+ if (!InterfaceMode.L2.equals(accessLists.getMode())) {
+ LOG.debug("Writing egress Acls is supported only in L2 mode. Ignoring config: {}", dataAfter);
+ return;
+ }
+
+ aclWriter
+ .write(id, subInterfaceIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(),
+ writeContext, getNumberOfTags(subInterface.getTags()), writeContext.getMappingContext());
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore,
+ @Nonnull final Egress dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("Sub-interface ACLs update: removing previously configured ACLs");
+ deleteCurrentAttributes(id, dataBefore, writeContext);
+ LOG.debug("Sub-interface ACLs update: adding updated ACLs");
+ writeCurrentAttributes(id, dataAfter, writeContext);
+ LOG.debug("Sub-interface ACLs update was successful");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Egress> id, @Nonnull final Egress dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String subInterfaceName = getSubInterfaceName(id);
+ final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext());
+ LOG.debug("Removing ACLs for sub-interface={}(id={}): {}", subInterfaceName, subInterfaceIndex, dataBefore);
+ aclWriter.deleteAcl(id, subInterfaceIndex, writeContext.getMappingContext());
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclCustomizer.java
new file mode 100644
index 000000000..fb2c2180d
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclCustomizer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.vpp.classifier.context.VppClassifierContextManager;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.acl.Ingress;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Customizer for enabling/disabling ingress ACLs on given interface based on low lever classfier model.
+ */
+public class AclCustomizer extends FutureJVppCustomizer implements WriterCustomizer<Ingress>, AclWriter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AclCustomizer.class);
+ private final NamingContext interfaceContext;
+ private final VppClassifierContextManager classifyTableContext;
+
+ public AclCustomizer(@Nonnull final FutureJVppCore vppApi, @Nonnull final NamingContext interfaceContext,
+ @Nonnull final VppClassifierContextManager classifyTableContext) {
+ super(vppApi);
+ this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null");
+ this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ setAcl(true, id, dataAfter, writeContext);
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore,
+ @Nonnull final Ingress dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ throw new UnsupportedOperationException("Acl update is not supported. Please delete Acl container first.");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ setAcl(false, id, dataBefore, writeContext);
+ }
+
+ private void setAcl(final boolean isAdd, @Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress acl,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String ifName = id.firstKeyOf(Interface.class).getName();
+ final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext());
+
+ LOG.debug("Setting ACL(isAdd={}) on interface={}(id={}): {}", isAdd, ifName, ifIndex, acl);
+
+ inputAclSetInterface(getFutureJVpp(), isAdd, id, acl, ifIndex, classifyTableContext,
+ writeContext.getMappingContext());
+ LOG.debug("Successfully set ACL(isAdd={}) on interface={}(id={}): {}", isAdd, ifName, ifIndex, acl);
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclWriter.java
new file mode 100644
index 000000000..7a3e5c5a0
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/AclWriter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.hc2vpp.common.translate.util.ByteDataTranslator;
+import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer;
+import io.fd.hc2vpp.vpp.classifier.context.VppClassifierContextManager;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.util.concurrent.CompletionStage;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.AclBaseAttributes;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.acl.base.attributes.Ip4Acl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.acl.base.attributes.Ip6Acl;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.acl.base.attributes.L2Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+interface AclWriter extends ByteDataTranslator, JvppReplyConsumer {
+
+ default void inputAclSetInterface(@Nonnull final FutureJVppCore futureJVppCore, final boolean isAdd,
+ @Nonnull final InstanceIdentifier<?> id, @Nonnull final AclBaseAttributes acl,
+ @Nonnegative final int ifIndex,
+ @Nonnull final VppClassifierContextManager classifyTableContext,
+ @Nonnull final MappingContext mappingContext) throws WriteFailedException {
+ final InputAclSetInterface request = new InputAclSetInterface();
+ request.isAdd = booleanToByte(isAdd);
+ request.swIfIndex = ifIndex;
+ request.l2TableIndex = ~0; // skip
+ request.ip4TableIndex = ~0; // skip
+ request.ip6TableIndex = ~0; // skip
+
+ final L2Acl l2Acl = acl.getL2Acl();
+ if (l2Acl != null) {
+ final String tableName = checkNotNull(l2Acl.getClassifyTable(), "L2 classify table is null");
+ request.l2TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext);
+ }
+ final Ip4Acl ip4Acl = acl.getIp4Acl();
+ if (ip4Acl != null) {
+ final String tableName = checkNotNull(ip4Acl.getClassifyTable(), "IPv4 classify table is null");
+ request.ip4TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext);
+ }
+ final Ip6Acl ip6Acl = acl.getIp6Acl();
+ if (ip6Acl != null) {
+ final String tableName = checkNotNull(ip6Acl.getClassifyTable(), "IPv6 classify table is null");
+ request.ip6TableIndex = classifyTableContext.getTableIndex(tableName, mappingContext);
+ }
+
+ final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
+ futureJVppCore.inputAclSetInterface(request);
+
+ getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IetfAclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IetfAclCustomizer.java
new file mode 100644
index 000000000..842c1596f
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IetfAclCustomizer.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.ietf.acl.Ingress;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Customizer for enabling/disabling ingress ACLs for given interface (as defined in ietf-acl model).
+ *
+ * The customizer assumes it owns classify table management for interfaces where ietf-acl container is present. Using
+ * low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported and
+ * can result in unpredictable behaviour.
+ */
+public class IetfAclCustomizer implements WriterCustomizer<Ingress> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class);
+ private final IngressIetfAclWriter aclWriter;
+ private final NamingContext interfaceContext;
+
+ public IetfAclCustomizer(@Nonnull final IngressIetfAclWriter aclWriter,
+ @Nonnull final NamingContext interfaceContext) {
+ this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null");
+ this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String ifName = id.firstKeyOf(Interface.class).getName();
+ final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext());
+ LOG.debug("Adding ACLs for interface={}(id={}): {}", ifName, ifIndex, dataAfter);
+
+ final AccessLists accessLists = dataAfter.getAccessLists();
+ checkArgument(accessLists != null && accessLists.getAcl() != null,
+ "ietf-acl container does not define acl list");
+
+ aclWriter.write(id, ifIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(),
+ writeContext, writeContext.getMappingContext());
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ LOG.debug("ACLs update: removing previously configured ACLs");
+ deleteCurrentAttributes(id, dataBefore, writeContext);
+ LOG.debug("ACLs update: adding updated ACLs");
+ writeCurrentAttributes(id, dataAfter, writeContext);
+ LOG.debug("ACLs update was successful");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String ifName = id.firstKeyOf(Interface.class).getName();
+ final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext());
+ LOG.debug("Removing ACLs for interface={}(id={}): {}", ifName, ifIndex, dataBefore);
+ aclWriter.deleteAcl(id, ifIndex, writeContext.getMappingContext());
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IngressIetfAclWriter.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IngressIetfAclWriter.java
new file mode 100644
index 000000000..42b18aeb8
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/IngressIetfAclWriter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.Optional;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.AbstractIetfAclWriter;
+import io.fd.hc2vpp.vpp.classifier.write.acl.common.AclTableContextManager;
+import io.fd.honeycomb.translate.MappingContext;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterface;
+import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import java.util.List;
+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.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntry;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.context.rev161214.mapping.entry.context.attributes.acl.mapping.entry.context.mapping.table.MappingEntryBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+public final class IngressIetfAclWriter extends AbstractIetfAclWriter {
+ private final AclTableContextManager aclCtx;
+
+ public IngressIetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore, @Nonnull AclTableContextManager aclCtx) {
+ super(futureJVppCore);
+ this.aclCtx = checkNotNull(aclCtx, "aclCtx should not be null");
+ }
+
+ @Override
+ public void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex,
+ @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException {
+ Optional<MappingEntry> optional = aclCtx.getEntry(swIfIndex, mappingContext);
+ checkState(optional.isPresent(), "Removing ACL id=%s, but acl mapping entry is not present", id);
+ final MappingEntry entry = optional.get();
+ unassignClassifyTables(id, entry);
+ removeClassifyTables(id, entry);
+ aclCtx.removeEntry(swIfIndex, mappingContext);
+ }
+
+ private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id,
+ @Nonnull final MappingEntry entry)
+ throws WriteFailedException {
+ final InputAclSetInterface request = new InputAclSetInterface();
+ request.isAdd = 0;
+ request.swIfIndex = entry.getIndex();
+ request.l2TableIndex = entry.getL2TableId();
+ request.ip4TableIndex = entry.getIp4TableId();
+ request.ip6TableIndex = entry.getIp6TableId();
+ final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage =
+ jvpp.inputAclSetInterface(request);
+ getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id);
+ }
+
+ @Override
+ public void write(@Nonnull final InstanceIdentifier<?> id, int swIfIndex, @Nonnull final List<Acl> acls,
+ @Nonnull final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode,
+ @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags,
+ @Nonnull final MappingContext mappingContext)
+ throws WriteFailedException {
+ checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range");
+
+ final InputAclSetInterface request = new InputAclSetInterface();
+ request.isAdd = 1;
+ request.swIfIndex = swIfIndex;
+ request.l2TableIndex = NOT_DEFINED;
+ request.ip4TableIndex = NOT_DEFINED;
+ request.ip6TableIndex = NOT_DEFINED;
+
+ if (InterfaceMode.L2.equals(mode)) {
+ final List<Ace> aces = getACEs(acls, writeContext, ace -> true);
+ request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags);
+ } else {
+ final List<Ace> ip4Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp4Path));
+ request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags);
+ final List<Ace> ip6Aces = getACEs(acls, writeContext, (AbstractIetfAclWriter::appliesToIp6Path));
+ request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags);
+ }
+
+ final MappingEntry entry = new MappingEntryBuilder().setIndex(swIfIndex)
+ .setIp4TableId(request.ip4TableIndex)
+ .setIp6TableId(request.ip6TableIndex)
+ .setL2TableId(request.l2TableIndex)
+ .build();
+ aclCtx.addEntry(entry, mappingContext);
+
+ try {
+ getReplyForWrite(jvpp.inputAclSetInterface(request).toCompletableFuture(), id);
+ } catch (WriteFailedException e) {
+ removeClassifyTables(id, entry);
+ throw e;
+ }
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceAclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceAclCustomizer.java
new file mode 100644
index 000000000..d864a9811
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceAclCustomizer.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import io.fd.hc2vpp.common.translate.util.FutureJVppCustomizer;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.v3po.util.SubInterfaceUtils;
+import io.fd.hc2vpp.vpp.classifier.context.VppClassifierContextManager;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import io.fd.vpp.jvpp.core.future.FutureJVppCore;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.acl.Ingress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Customizer for enabling/disabling ingress ACLs on given sub-interface.
+ */
+public class SubInterfaceAclCustomizer extends FutureJVppCustomizer
+ implements WriterCustomizer<Ingress>, AclWriter {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceAclCustomizer.class);
+ private final NamingContext interfaceContext;
+ private final VppClassifierContextManager classifyTableContext;
+
+ public SubInterfaceAclCustomizer(@Nonnull final FutureJVppCore vppApi,
+ @Nonnull final NamingContext interfaceContext,
+ @Nonnull final VppClassifierContextManager classifyTableContext) {
+ super(vppApi);
+ this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null");
+ this.classifyTableContext = checkNotNull(classifyTableContext, "classifyTableContext should not be null");
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ setAcl(true, id, dataAfter, writeContext);
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore,
+ @Nonnull final Ingress dataAfter, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ throw new UnsupportedOperationException("Acl update is not supported. Please delete Acl container first.");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ setAcl(false, id, dataBefore, writeContext);
+ }
+
+ private void setAcl(final boolean isAdd, @Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress acl,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class);
+ final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class);
+ final String subInterfaceName = SubInterfaceUtils
+ .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue());
+ final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext());
+
+ LOG.debug("Setting ACL(isAdd={}) on sub-interface={}(id={}): {}",
+ isAdd, subInterfaceName, subInterfaceIndex, acl);
+ inputAclSetInterface(getFutureJVpp(), isAdd, id, acl, subInterfaceIndex, classifyTableContext,
+ writeContext.getMappingContext());
+ LOG.debug("Successfully set ACL(isAdd={}) on sub-interface={}(id={}): {}",
+ isAdd, subInterfaceName, subInterfaceIndex, acl);
+ }
+}
diff --git a/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceIetfAclCustomizer.java b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceIetfAclCustomizer.java
new file mode 100644
index 000000000..6f161b858
--- /dev/null
+++ b/vpp-classifier/impl/src/main/java/io/fd/hc2vpp/vpp/classifier/write/acl/ingress/SubInterfaceIetfAclCustomizer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.vpp.classifier.write.acl.ingress;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static io.fd.hc2vpp.v3po.util.SubInterfaceUtils.getNumberOfTags;
+
+import com.google.common.base.Optional;
+import io.fd.hc2vpp.common.translate.util.NamingContext;
+import io.fd.hc2vpp.v3po.util.SubInterfaceUtils;
+import io.fd.honeycomb.translate.spi.write.WriterCustomizer;
+import io.fd.honeycomb.translate.write.WriteContext;
+import io.fd.honeycomb.translate.write.WriteFailedException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.classifier.acl.rev161214.vpp.acl.attributes.ietf.acl.Ingress;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterface;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.interfaces._interface.sub.interfaces.SubInterfaceKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Customizer for enabling/disabling ingress ACLs for given sub-interface (as defined in ietf-acl model).
+ *
+ * The customizer assumes it owns classify table management for sub-interfaces where ietf-acl container is present.
+ * Using low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported
+ * and can result in unpredictable behaviour.
+ */
+public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Ingress> {
+
+ private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class);
+ private final IngressIetfAclWriter aclWriter;
+ private final NamingContext interfaceContext;
+
+ public SubInterfaceIetfAclCustomizer(@Nonnull final IngressIetfAclWriter aclWriter,
+ @Nonnull final NamingContext interfaceContext) {
+ this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null");
+ this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null");
+ }
+
+ private String getSubInterfaceName(@Nonnull final InstanceIdentifier<Ingress> id) {
+ final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class);
+ final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class);
+ return SubInterfaceUtils
+ .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue());
+ }
+
+ @Override
+ public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ final String subInterfaceName = getSubInterfaceName(id);
+ final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext());
+ LOG.debug("Adding IETF-ACL for sub-interface: {}(id={}): {}", subInterfaceName, subInterfaceIndex, dataAfter);
+
+ final AccessLists accessLists = dataAfter.getAccessLists();
+ checkArgument(accessLists != null && accessLists.getAcl() != null,
+ "ietf-acl container does not define acl list");
+
+ final Optional<SubInterface> subInterfaceOptional =
+ writeContext.readAfter(id.firstIdentifierOf(SubInterface.class));
+ checkState(subInterfaceOptional.isPresent(), "Could not read SubInterface data object for %s", id);
+ final SubInterface subInterface = subInterfaceOptional.get();
+
+ aclWriter
+ .write(id, subInterfaceIndex, accessLists.getAcl(), accessLists.getDefaultAction(), accessLists.getMode(),
+ writeContext, getNumberOfTags(subInterface.getTags()), writeContext.getMappingContext());
+ }
+
+ @Override
+ public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore, @Nonnull final Ingress dataAfter,
+ @Nonnull final WriteContext writeContext) throws WriteFailedException {
+ LOG.debug("Sub-interface ACLs update: removing previously configured ACLs");
+ deleteCurrentAttributes(id, dataBefore, writeContext);
+ LOG.debug("Sub-interface ACLs update: adding updated ACLs");
+ writeCurrentAttributes(id, dataAfter, writeContext);
+ LOG.debug("Sub-interface ACLs update was successful");
+ }
+
+ @Override
+ public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Ingress> id,
+ @Nonnull final Ingress dataBefore, @Nonnull final WriteContext writeContext)
+ throws WriteFailedException {
+ final String subInterfaceName = getSubInterfaceName(id);
+ final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext());
+ LOG.debug("Removing ACLs for sub-interface={}(id={}): {}", subInterfaceName, subInterfaceIndex, dataBefore);
+ aclWriter.deleteAcl(id, subInterfaceIndex, writeContext.getMappingContext());
+ }
+}