summaryrefslogtreecommitdiffstats
path: root/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common
diff options
context:
space:
mode:
Diffstat (limited to 'v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common')
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java251
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java85
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java94
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java99
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java129
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java54
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java53
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java68
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java74
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java47
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java149
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java182
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java90
-rw-r--r--v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java126
14 files changed, 1501 insertions, 0 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java
new file mode 100644
index 000000000..29e92ae2a
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AbstractIetfAclWriter.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public 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.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child(
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class,
+ new AclKey(aclName, aclType)));
+ checkArgument(aclOptional.isPresent(), "Acl lists not configured");
+ final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl
+ acl = aclOptional.get();
+
+ final AccessListEntries accessListEntries = acl.getAccessListEntries();
+ checkArgument(accessListEntries != null, "access list entries not configured");
+
+ return accessListEntries.getAce().stream();
+ }
+
+ 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.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)
+ .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) {
+ return true;
+ }
+ return false;
+ }
+
+ 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)
+ .getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) {
+ return true;
+ }
+ return false;
+ }
+
+ 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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java
new file mode 100644
index 000000000..67351e0c6
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceEthWriter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java
new file mode 100644
index 000000000..78ed33aa4
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp4Writer.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java
new file mode 100644
index 000000000..d7cbb0009
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIp6Writer.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java
new file mode 100644
index 000000000..164493cc5
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceIpAndEthWriter.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.AceIpVersion;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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.getAceIpVersion() != null) {
+ if (ace.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 int numberOfSessions = PortPair.fromRange(ace.getSourcePortRange(), ace.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(ace.getDestinationMacAddressMask(), ace.getDestinationMacAddress(), request);
+ aceIsEmpty &= sourceMacAddressMask(ace.getSourceMacAddressMask(), ace.getSourceMacAddress(), request);
+
+ // if we use classifier API, we need to know ip version (fields common for ip4 and ip6 have different offsets):
+ final AceIpVersion aceIpVersion = ace.getAceIpVersion();
+ checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion");
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ if (aceIpVersion instanceof AceIpv4) {
+ final AceIpv4 ipVersion = (AceIpv4) aceIpVersion;
+ aceIsEmpty &= ip4Mask(baseOffset, mode, ace, ipVersion, request);
+ } else if (aceIpVersion instanceof AceIpv6) {
+ final AceIpv6 ipVersion = (AceIpv6) aceIpVersion;
+ aceIsEmpty &= ip6Mask(baseOffset, mode, ace, 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 List<PortPair> portPairs = PortPair.fromRange(ace.getSourcePortRange(), ace.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(ace.getDestinationMacAddress(), request);
+ noMatch &= sourceMacAddressMatch(ace.getSourceMacAddress(), request);
+
+ final AceIpVersion aceIpVersion = ace.getAceIpVersion();
+ checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion");
+
+ final int baseOffset = getVlanTagsLen(vlanTags);
+ if (aceIpVersion instanceof AceIpv4) {
+ final AceIpv4 ipVersion = (AceIpv4) aceIpVersion;
+ noMatch &= ip4Match(baseOffset, mode, ace, ipVersion, pair.getSrc(), pair.getDst(), request);
+ } else if (aceIpVersion instanceof AceIpv6) {
+ final AceIpv6 ipVersion = (AceIpv6) aceIpVersion;
+ noMatch &= ip6Match(baseOffset, mode, ace, 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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java
new file mode 100644
index 000000000..75a10e40f
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AceWriter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java
new file mode 100644
index 000000000..0728944f2
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java
new file mode 100644
index 000000000..659244fda
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTableContextManagerImpl.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java
new file mode 100644
index 000000000..e4291122d
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/AclTranslator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java
new file mode 100644
index 000000000..df0b65c2e
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/IetfAclWriter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.acl.rev161214.InterfaceMode;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+
+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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java
new file mode 100644
index 000000000..c1bad0735
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip4AclTranslator.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java
new file mode 100644
index 000000000..ec9bebe7e
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/Ip6AclTranslator.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java
new file mode 100644
index 000000000..5a6807bba
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/L2AclTranslator.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java
new file mode 100644
index 000000000..1630f58fd
--- /dev/null
+++ b/v3po/v3po2vpp/src/main/java/io/fd/hc2vpp/v3po/interfaces/acl/common/PortPair.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2016 Cisco and/or its affiliates.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.fd.hc2vpp.v3po.interfaces.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));
+ }
+ }
+ }
+}