diff options
author | Marek Gradzki <mgradzki@cisco.com> | 2016-10-10 14:55:15 +0200 |
---|---|---|
committer | Maros Marsalek <mmarsale@cisco.com> | 2016-10-13 11:27:29 +0000 |
commit | a55da7924adda3e82f6e5be40e01084c65e93ac0 (patch) | |
tree | ee9d6f6ff1144186c0e13e111ac35b435fb9ad75 /v3po/v3po2vpp/src/main/java | |
parent | 5543d61420bd198dc34e8f0e64c3479c185a9c2b (diff) |
HONEYCOMB-233: add support for mixing L2/L3 rules
In case of L2 interfaces, acls are translated into
a chain of classify tables and assigned as L2 table.
In case of L3 interfaces, acls are translated into
ip4 and ip6 chains (eth only rules go to
both chains, rest - depending on ip-version).
Limitations:
- it is not possible to define L3 rule without specifying ip-version
(common header fields for IP4/IP6 have different offsets),
- eth rules on L3 interfaces are applied only to IP traffic
(vpp classfier limitation).
Change-Id: I7ca2648cabad8c6e936cf71a51e06596a42891e8
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Diffstat (limited to 'v3po/v3po2vpp/src/main/java')
19 files changed, 820 insertions, 682 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java index 8e001b75d..301c30501 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java @@ -18,10 +18,10 @@ package io.fd.honeycomb.translate.v3po; import com.google.inject.Inject; import com.google.inject.Provider; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.vpp.jvpp.core.future.FutureJVppCore; -class IetfAClWriterProvider implements Provider<IetfAClWriter> { +class IetfAClWriterProvider implements Provider<IetfAclWriter> { private final FutureJVppCore jvpp; @@ -31,7 +31,7 @@ class IetfAClWriterProvider implements Provider<IetfAClWriter> { } @Override - public IetfAClWriter get() { - return new IetfAClWriter(jvpp); + public IetfAclWriter get() { + return new IetfAclWriter(jvpp); } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java index 9c46ba316..be3801bd2 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/InterfacesWriterFactory.java @@ -35,7 +35,7 @@ import io.fd.honeycomb.translate.v3po.interfaces.VhostUserCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.VxlanCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.VxlanGpeCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4AddressCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4Customizer; @@ -94,7 +94,7 @@ public final class InterfacesWriterFactory implements WriterFactory { org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev161214.interfaces._interface.ietf.acl.Egress.class); private final FutureJVppCore jvpp; - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext bdNamingContext; private final NamingContext ifcNamingContext; private final VppClassifierContextManager classifyTableContext; @@ -102,7 +102,7 @@ public final class InterfacesWriterFactory implements WriterFactory { @Inject public InterfacesWriterFactory(final FutureJVppCore vppJvppIfcDependency, - final IetfAClWriter aclWriter, + final IetfAclWriter aclWriter, @Named("bridge-domain-context") final NamingContext bridgeDomainContextDependency, @Named("interface-context") final NamingContext interfaceContextDependency, @Named("classify-table-context") final VppClassifierContextManager classifyTableContext, diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java index f4dc07646..afa362d5c 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/SubinterfaceAugmentationWriterFactory.java @@ -26,7 +26,7 @@ import io.fd.honeycomb.translate.v3po.interfaces.RewriteCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.SubInterfaceAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.SubInterfaceCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.SubInterfaceL2Customizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.SubInterfaceIetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.SubInterfaceIpv4AddressCustomizer; import io.fd.honeycomb.translate.vpp.util.NamingContext; @@ -59,7 +59,7 @@ import io.fd.vpp.jvpp.core.future.FutureJVppCore; public final class SubinterfaceAugmentationWriterFactory implements WriterFactory { private final FutureJVppCore jvpp; - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext ifcContext; private final NamingContext bdContext; private final VppClassifierContextManager classifyTableContext; @@ -79,7 +79,7 @@ public final class SubinterfaceAugmentationWriterFactory implements WriterFactor org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev161214.sub._interface.base.attributes.ietf.acl.Egress.class); public SubinterfaceAugmentationWriterFactory(final FutureJVppCore jvpp, - final IetfAClWriter aclWriter, + final IetfAclWriter aclWriter, final NamingContext ifcContext, final NamingContext bdContext, final VppClassifierContextManager classifyTableContext) { this.jvpp = jvpp; this.aclWriter = aclWriter; diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java index de29d5d2c..eaecdf025 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/V3poModule.java @@ -26,7 +26,7 @@ import io.fd.honeycomb.translate.v3po.cfgattrs.V3poConfiguration; import io.fd.honeycomb.translate.v3po.initializers.InterfacesInitializer; import io.fd.honeycomb.translate.v3po.initializers.VppClassifierInitializer; import io.fd.honeycomb.translate.v3po.initializers.VppInitializer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.v3po.notification.InterfaceChangeNotificationProducer; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.v3po.vppclassifier.VppClassifierContextManager; @@ -59,7 +59,7 @@ public class V3poModule extends AbstractModule { bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(1)); // Utils - bind(IetfAClWriter.class).toProvider(IetfAClWriterProvider.class); + bind(IetfAclWriter.class).toProvider(IetfAClWriterProvider.class); // Context utility for deleted interfaces bind(DisabledInterfacesManager.class).toInstance(new DisabledInterfacesManager()); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java index 0eff83b00..3dc9e2262 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/IetfAclCustomizer.java @@ -19,7 +19,7 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.egress; import static com.google.common.base.Preconditions.checkNotNull; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; @@ -31,11 +31,11 @@ import org.slf4j.LoggerFactory; public class IetfAclCustomizer implements WriterCustomizer<Egress> { private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public IetfAclCustomizer(final IetfAClWriter aclWriter, final NamingContext interfaceContext) { + public IetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java index 2a20a754f..b5198465a 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/egress/SubInterfaceIetfAclCustomizer.java @@ -19,7 +19,7 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.egress; import static com.google.common.base.Preconditions.checkNotNull; import io.fd.honeycomb.translate.spi.write.WriterCustomizer; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; @@ -31,10 +31,10 @@ import org.slf4j.LoggerFactory; public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Egress> { private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public SubInterfaceIetfAclCustomizer(final IetfAClWriter aclWriter, final NamingContext interfaceContext) { + public SubInterfaceIetfAclCustomizer(final IetfAclWriter aclWriter, final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java deleted file mode 100644 index a5bbab545..000000000 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AbstractAceWriter.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Copyright (c) 2016 Cisco and/or its affiliates. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; - -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkNotNull; - -import com.google.common.annotations.VisibleForTesting; -import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer; -import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; -import java.util.List; -import java.util.ListIterator; -import java.util.concurrent.CompletionStage; -import javax.annotation.Nonnegative; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; - -/** - * Base writer for translation of ietf-acl model ACEs to VPP's classify tables and sessions. <p/> Creates one classify - * table with single session per ACE. - * - * @param <T> type of access control list entry - */ -abstract class AbstractAceWriter<T extends AceType> implements AceWriter, JvppReplyConsumer { - - // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model - // mapping more convenient): - // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table, - // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, so setting 8K for now - protected static final int TABLE_MEM_SIZE = 8 * 1024; - - @VisibleForTesting - static final int VLAN_TAG_LEN = 4; - - private static final int PERMIT = -1; - private static final int DENY = 0; - - private final FutureJVppCore futureJVppCore; - - public AbstractAceWriter(@Nonnull final FutureJVppCore futureJVppCore) { - this.futureJVppCore = checkNotNull(futureJVppCore, "futureJVppCore should not be null"); - } - - /** - * Creates classify table for given ACE. - * - * @param ace ACE to be translated - * @param mode interface mode - * @param nextTableIndex classify table index - * @param vlanTags number of vlan tags - * @return classify table that represents given ACE - */ - protected abstract ClassifyAddDelTable createClassifyTable(@Nonnull final T ace, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags); - - /** - * Creates classify session for given ACE. - * - * @param action packet handling action (permit/deny) - * @param ace ACE to be translated - * @param mode interface mode - * @param tableIndex classify table index for the given session - * @param vlanTags number of vlan tags - * @return classify session that represents given ACE - */ - protected abstract ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final T ace, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags); - - /** - * Sets classify table index for input_acl_set_interface request. - * - * @param request request DTO - * @param tableIndex pointer to a chain of classify tables - */ - protected abstract void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex); - - @Override - public final void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces, - final InterfaceMode mode, final AccessLists.DefaultAction defaultAction, - @Nonnull final InputAclSetInterface request, - @Nonnegative final int vlanTags) - throws WriteFailedException { - - checkArgument(vlanTags >= 0 && vlanTags <= 2, "Number of vlan tags %s is not in [0,2] range"); - int nextTableIndex = configureDefaltAction(id, defaultAction); - - final ListIterator<Ace> iterator = aces.listIterator(aces.size()); - while (iterator.hasPrevious()) { - // Create table + session per entry - final Ace ace = iterator.previous(); - final PacketHandling action = ace.getActions().getPacketHandling(); - final T type = (T)ace.getMatches().getAceType(); - final ClassifyAddDelTable ctRequest = createClassifyTable(type, mode, nextTableIndex, vlanTags); - nextTableIndex = createClassifyTable(id, ctRequest); - createClassifySession(id, createClassifySession(action, type, mode, nextTableIndex, vlanTags)); - } - setClassifyTable(request, nextTableIndex); - } - - private int configureDefaltAction(@Nonnull final InstanceIdentifier<?> id, final AccessLists.DefaultAction defaultAction) - throws WriteFailedException { - ClassifyAddDelTable ctRequest = createClassifyTable(-1); - if (AccessLists.DefaultAction.Permit.equals(defaultAction)) { - ctRequest.missNextIndex = PERMIT; - } else { - ctRequest.missNextIndex = DENY; - } - ctRequest.mask = new byte[16]; - ctRequest.skipNVectors = 0; - ctRequest.matchNVectors = 1; - return createClassifyTable(id, ctRequest); - } - - private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id, - @Nonnull final ClassifyAddDelTable request) - throws WriteFailedException { - final CompletionStage<ClassifyAddDelTableReply> cs = futureJVppCore.classifyAddDelTable(request); - - final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id); - return reply.newTableIndex; - } - - private void createClassifySession(@Nonnull final InstanceIdentifier<?> id, - @Nonnull final ClassifyAddDelSession request) - throws WriteFailedException { - final CompletionStage<ClassifyAddDelSessionReply> cs = futureJVppCore.classifyAddDelSession(request); - - getReplyForWrite(cs.toCompletableFuture(), id); - } - - protected ClassifyAddDelTable createClassifyTable(final int nextTableIndex) { - final ClassifyAddDelTable request = new ClassifyAddDelTable(); - request.isAdd = 1; - request.tableIndex = -1; // value not present - request.nbuckets = 1; // we expect exactly one session per table - request.nextTableIndex = nextTableIndex; - request.memorySize = TABLE_MEM_SIZE; - request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain - return request; - } - - protected ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, final int tableIndex) { - final ClassifyAddDelSession request = new ClassifyAddDelSession(); - request.isAdd = 1; - request.tableIndex = tableIndex; - request.opaqueIndex = ~0; // value not used - - if (action instanceof Permit) { - request.hitNextIndex = -1; - } // deny (0) is default value - - return request; - } - - protected int getVlanTagsLen(final int vlanTags) { - return vlanTags * VLAN_TAG_LEN; - } -} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java index 939e4eb98..91ab927fb 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriter.java @@ -16,15 +16,9 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static com.google.common.base.Preconditions.checkArgument; - import com.google.common.annotations.VisibleForTesting; -import io.fd.honeycomb.translate.vpp.util.MacTranslator; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; -import java.util.List; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; @@ -33,69 +27,29 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceEthWriter extends AbstractAceWriter<AceEth> implements MacTranslator { +final class AceEthWriter implements AceWriter<AceEth>, AclTranslator, L2AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 1; private static final Logger LOG = LoggerFactory.getLogger(AceEthWriter.class); - public AceEthWriter(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static void checkInterfaceMode(@Nullable final InterfaceMode mode) { - checkArgument(InterfaceMode.L2.equals(mode), "L2 rules are not allowed for interface in L3 mode"); - } - @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceEth aceEth, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { - checkInterfaceMode(mode); - - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + public ClassifyAddDelTable createTable(@Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { + final ClassifyAddDelTable request = createTable(nextTableIndex); request.mask = new byte[16]; - boolean aceIsEmpty = true; - - // destination-mac-address or destination-mac-address-mask is present => - // ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00 - if (aceEth.getDestinationMacAddressMask() != null) { - aceIsEmpty = false; - final String macAddress = aceEth.getDestinationMacAddressMask().getValue(); - final List<String> parts = COLON_SPLITTER.splitToList(macAddress); - int i = 0; - for (String part : parts) { - request.mask[i++] = parseHexByte(part); - } - } else if (aceEth.getDestinationMacAddress() != null) { - aceIsEmpty = false; - for (int i = 0; i < 6; ++i) { - request.mask[i] = (byte) 0xff; - } - } - - // source-mac-address or source-mac-address-mask => - // 00:00:00:00:00:00:ff:ff:ff:ff:ff:ff:00:00:00:00 - if (aceEth.getSourceMacAddressMask() != null) { - aceIsEmpty = false; - final String macAddress = aceEth.getSourceMacAddressMask().getValue(); - final List<String> parts = COLON_SPLITTER.splitToList(macAddress); - int i = 6; - for (String part : parts) { - request.mask[i++] = parseHexByte(part); - } - } else if (aceEth.getSourceMacAddress() != null) { - aceIsEmpty = false; - for (int i = 6; i < 12; ++i) { - request.mask[i] = (byte) 0xff; - } - } + boolean aceIsEmpty = + destinationMacAddressMask(aceEth.getDestinationMacAddressMask(), aceEth.getDestinationMacAddress(), + request); + aceIsEmpty &= + sourceMacAddressMask(aceEth.getSourceMacAddressMask(), aceEth.getSourceMacAddress(), request); if (aceIsEmpty) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceEth.toString())); + String.format("Ace %s does not define packet field match values", aceEth.toString())); } request.skipNVectors = 0; @@ -106,50 +60,24 @@ final class AceEthWriter extends AbstractAceWriter<AceEth> implements MacTransla } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceEth aceEth, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { - checkInterfaceMode(mode); - - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceEth aceEth, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[16]; - boolean noMatch = true; - - if (aceEth.getDestinationMacAddress() != null) { - noMatch = false; - final String macAddress = aceEth.getDestinationMacAddress().getValue(); - final List<String> parts = COLON_SPLITTER.splitToList(macAddress); - int i = 0; - for (String part : parts) { - request.match[i++] = parseHexByte(part); - } - } - - if (aceEth.getSourceMacAddress() != null) { - noMatch = false; - final String macAddress = aceEth.getSourceMacAddress().getValue(); - final List<String> parts = COLON_SPLITTER.splitToList(macAddress); - int i = 6; - for (String part : parts) { - request.match[i++] = parseHexByte(part); - } - } + boolean noMatch = destinationMacAddressMatch(aceEth.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(aceEth.getSourceMacAddress(), request); if (noMatch) { throw new IllegalArgumentException( - String.format("Ace %s does not define neither source nor destination MAC address", - aceEth.toString())); + String.format("Ace %s does not define neither source nor destination MAC address", + aceEth.toString())); } LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceEth, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.l2TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java index 2f8d030ae..affc8735d 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4Writer.java @@ -19,121 +19,42 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.VisibleForTesting; -import com.google.common.primitives.Ints; -import io.fd.honeycomb.translate.vpp.util.Ipv4Translator; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceIp4Writer extends AbstractAceWriter<AceIp> implements Ipv4Translator { +final class AceIp4Writer implements AceWriter<AceIp>, AclTranslator, Ip4AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 3; // number of 16B vectors - private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class); private static final int TABLE_MASK_LENGTH = 48; - private static final int IP4_MASK_BIT_LENGTH = 32; - - private static final int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) - private static final int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET+2; - private static final int DSCP_OFFSET = 15; - private static final int DSCP_MASK = 0xfc; - private static final int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET+9; - private static final int IP_PROTOCOL_MASK = 0xff; - private static final int IP4_LEN = 4; - private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 12; - private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN; - - public AceIp4Writer(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static byte[] toByteMask(final int prefixLength) { - final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength); - return Ints.toByteArray((int) mask); - } - - private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) { - final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]); - return toByteMask(prefixLength); - } - - // static removed, cant use default from static content - private byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) { - final String[] split = ipv4Prefix.getValue().split("/"); - final byte[] addressBytes = ipv4AddressNoZoneToArray(split[0]); - final byte[] mask = toByteMask(Byte.valueOf(split[1])); - for (int i = 0; i < addressBytes.length; ++i) { - addressBytes[i] &= mask[i]; - } - return addressBytes; - } + private static final Logger LOG = LoggerFactory.getLogger(AceIp4Writer.class); @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + final ClassifyAddDelTable request = createTable(nextTableIndex); request.skipNVectors = 0; // match entire L2 and L3 header request.matchNVectors = MATCH_N_VECTORS; - - boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // in L2 mode we need to match ether type - request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; - request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; - } - - if (aceIp.getDscp() != null) { - aceIsEmpty = false; - request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits - } - - if (aceIp.getProtocol() != null) { // Internet Protocol number - request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getSourceIpv4Network() != null) { - aceIsEmpty = false; - System.arraycopy(toByteMask(ipVersion.getSourceIpv4Network()), 0, request.mask, baseOffset + SRC_IP_OFFSET, - IP4_LEN); - } - - if (ipVersion.getDestinationIpv4Network() != null) { - aceIsEmpty = false; - System - .arraycopy(toByteMask(ipVersion.getDestinationIpv4Network()), 0, request.mask, - baseOffset + DST_IP_OFFSET, IP4_LEN); - } - + boolean aceIsEmpty = ip4Mask(baseOffset, mode, aceIp, ipVersion, request, LOG); if (aceIsEmpty) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceIp.toString())); + String.format("Ace %s does not define packet field match values", aceIp.toString())); } LOG.debug("ACE rule={} translated to table={}.", aceIp, request); @@ -141,70 +62,25 @@ final class AceIp4Writer extends AbstractAceWriter<AceIp> implements Ipv4Transla } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv4, "Expected AceIpv4 version, but was %", aceIp); final AceIpv4 ipVersion = (AceIpv4) aceIp.getAceIpVersion(); - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); - + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[TABLE_MASK_LENGTH]; - boolean noMatch = true; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // match IP4 etherType (0x0800) - request.match[baseOffset + ETHER_TYPE_OFFSET] = 0x08; - request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = 0x00; - } - - if (aceIp.getProtocol() != null) { - request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & aceIp.getProtocol()); - } - - if (aceIp.getDscp() != null) { - noMatch = false; - request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (aceIp.getDscp().getValue() << 2)); - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getSourceIpv4Network() != null) { - noMatch = false; - System - .arraycopy(toMatchValue(ipVersion.getSourceIpv4Network()), 0, request.match, - baseOffset + SRC_IP_OFFSET, - IP4_LEN); - } - - if (ipVersion.getDestinationIpv4Network() != null) { - noMatch = false; - System.arraycopy(toMatchValue(ipVersion.getDestinationIpv4Network()), 0, request.match, - baseOffset + DST_IP_OFFSET, - IP4_LEN); - } - + boolean noMatch = ip4Match(baseOffset, mode, aceIp, ipVersion, request, LOG); if (noMatch) { throw new IllegalArgumentException( - String.format("Ace %s does not define packet field match values", aceIp.toString())); + String.format("Ace %s does not define packet field match values", aceIp.toString())); } LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.ip4TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java index f1cccba92..b1186440d 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6Writer.java @@ -19,142 +19,39 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; import static com.google.common.base.Preconditions.checkArgument; import com.google.common.annotations.VisibleForTesting; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.BitSet; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; -import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -final class AceIp6Writer extends AbstractAceWriter<AceIp> { +final class AceIp6Writer implements AceWriter<AceIp>, AclTranslator, Ip6AclTranslator { @VisibleForTesting static final int MATCH_N_VECTORS = 4; // number of 16B vectors - private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class); private static final int TABLE_MASK_LENGTH = 64; - private static final int IP6_MASK_BIT_LENGTH = 128; - - private static final int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) - private static final int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET+2; - private static final int DSCP_MASK1 = 0x0f; - private static final int DSCP_MASK2 = 0xc0; - private static final int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET+6; - private static final int IP_PROTOCOL_MASK = 0xff; - private static final int IP6_LEN = 16; - private static final int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8; - private static final int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN; - - public AceIp6Writer(@Nonnull final FutureJVppCore futureJVppCore) { - super(futureJVppCore); - } - - private static byte[] toByteMask(final int prefixLength) { - final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH); - mask.set(0, prefixLength, true); - if (prefixLength < IP6_MASK_BIT_LENGTH) { - mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false); - } - return mask.toByteArray(); - } - - private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) { - final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]); - return toByteMask(prefixLength); - } - - private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) { - final String[] split = ipv6Prefix.getValue().split("/"); - final byte[] addressBytes; - try { - addressBytes = InetAddress.getByName(split[0]).getAddress(); - } catch (UnknownHostException e) { - throw new IllegalArgumentException("Invalid IP6 address", e); - } - final byte[] mask = toByteMask(Short.valueOf(split[1])); - int pos = 0; - for (; pos < mask.length; ++pos) { - addressBytes[pos] &= mask[pos]; - } - // mask can be shorter that address, so we need to clear rest of the address: - for (; pos < addressBytes.length; ++pos) { - addressBytes[pos] = 0; - } - return addressBytes; - } + private static final Logger LOG = LoggerFactory.getLogger(AceIp6Writer.class); @Override - public ClassifyAddDelTable createClassifyTable(@Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int nextTableIndex, - final int vlanTags) { + public ClassifyAddDelTable createTable(@Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int nextTableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); - final ClassifyAddDelTable request = createClassifyTable(nextTableIndex); + final ClassifyAddDelTable request = createTable(nextTableIndex); request.skipNVectors = 0; // match entire L2 and L3 header request.matchNVectors = MATCH_N_VECTORS; - - boolean aceIsEmpty = true; request.mask = new byte[TABLE_MASK_LENGTH]; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // in L2 mode we need to match ether type - request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; - request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; - } - - if (aceIp.getDscp() != null) { - aceIsEmpty = false; - // DCSP (bits 4-9 of IP6 header) - request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; - request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; - } - - if (aceIp.getProtocol() != null) { - aceIsEmpty = false; - request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getFlowLabel() != null) { - aceIsEmpty = false; - // bits 12-31 - request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f; - request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff; - request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff; - } - - if (ipVersion.getSourceIpv6Network() != null) { - aceIsEmpty = false; - final byte[] mask = toByteMask(ipVersion.getSourceIpv6Network()); - System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length); - } - - if (ipVersion.getDestinationIpv6Network() != null) { - aceIsEmpty = false; - final byte[] mask = toByteMask(ipVersion.getDestinationIpv6Network()); - System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); - } - + boolean aceIsEmpty = ip6Mask(baseOffset, mode, aceIp, ipVersion, request, LOG); if (aceIsEmpty) { throw new IllegalArgumentException( String.format("Ace %s does not define packet field match values", aceIp.toString())); @@ -165,68 +62,19 @@ final class AceIp6Writer extends AbstractAceWriter<AceIp> { } @Override - public ClassifyAddDelSession createClassifySession(@Nonnull final PacketHandling action, - @Nonnull final AceIp aceIp, - @Nullable final InterfaceMode mode, - final int tableIndex, - final int vlanTags) { + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIp aceIp, + @Nullable final InterfaceMode mode, + final int tableIndex, + final int vlanTags) { checkArgument(aceIp.getAceIpVersion() instanceof AceIpv6, "Expected AceIpv6 version, but was %", aceIp); final AceIpv6 ipVersion = (AceIpv6) aceIp.getAceIpVersion(); - final ClassifyAddDelSession request = createClassifySession(action, tableIndex); + final ClassifyAddDelSession request = createSession(action, tableIndex); request.match = new byte[TABLE_MASK_LENGTH]; - boolean noMatch = true; final int baseOffset = getVlanTagsLen(vlanTags); - - if (InterfaceMode.L2.equals(mode)) { - // match IP6 etherType (0x86dd) - request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86; - request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd; - } - - if (aceIp.getDscp() != null) { - noMatch = false; - final int dscp = aceIp.getDscp().getValue(); - // set bits 4-9 of IP6 header: - request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dscp >> 2)); - request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dscp << 6)); - } - - if (aceIp.getProtocol() != null) { - noMatch = false; - request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & aceIp.getProtocol()); - } - - if (aceIp.getSourcePortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getSourcePortRange()); - } - - if (aceIp.getDestinationPortRange() != null) { - LOG.warn("L4 Header fields are not supported. Ignoring {}", aceIp.getDestinationPortRange()); - } - - if (ipVersion.getFlowLabel() != null) { - noMatch = false; - final int flowLabel = ipVersion.getFlowLabel().getValue().intValue(); - // bits 12-31 - request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16)); - request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8)); - request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel); - } - - if (ipVersion.getSourceIpv6Network() != null) { - noMatch = false; - final byte[] match = toMatchValue(ipVersion.getSourceIpv6Network()); - System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN); - } - - if (ipVersion.getDestinationIpv6Network() != null) { - noMatch = false; - final byte[] match = toMatchValue(ipVersion.getDestinationIpv6Network()); - System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); - } - + boolean noMatch = ip6Match(baseOffset, mode, aceIp, ipVersion, request, LOG); if (noMatch) { throw new IllegalArgumentException( String.format("Ace %s does not define packet field match values", aceIp.toString())); @@ -235,9 +83,4 @@ final class AceIp6Writer extends AbstractAceWriter<AceIp> { LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceIp, request); return request; } - - @Override - protected void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex) { - request.ip6TableIndex = tableIndex; - } } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java new file mode 100644 index 000000000..40a050a6d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriter.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import static com.google.common.base.Preconditions.checkArgument; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.AceIpVersion; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class AceIpAndEthWriter + implements AceWriter<AceIpAndEth>, AclTranslator, L2AclTranslator, Ip4AclTranslator, Ip6AclTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AceIpAndEthWriter.class); + + private static int maskLength(@Nonnull final AceIpAndEth ace) { + if (ace.getAceIpVersion() != null) { + if (ace.getAceIpVersion() instanceof AceIpv4) { + return 48; + } else { + return 64; + } + } + return 16; + } + + @Override + public ClassifyAddDelTable createTable(@Nonnull final AceIpAndEth ace, @Nullable final InterfaceMode mode, + final int nextTableIndex, final int vlanTags) { + final ClassifyAddDelTable request = createTable(nextTableIndex); + final int maskLength = maskLength(ace); + request.mask = new byte[maskLength]; + request.skipNVectors = 0; + request.matchNVectors = maskLength / 16; + + boolean aceIsEmpty = + destinationMacAddressMask(ace.getDestinationMacAddressMask(), ace.getDestinationMacAddress(), request); + aceIsEmpty &= sourceMacAddressMask(ace.getSourceMacAddressMask(), ace.getSourceMacAddress(), request); + + // if we use classifier API, we need to know ip version (fields common for ip4 and ip6 have different offsets): + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + aceIsEmpty &= ip4Mask(baseOffset, mode, ace, ipVersion, request, LOG); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + aceIsEmpty &= ip6Mask(baseOffset, mode, ace, ipVersion, request, LOG); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + + LOG.debug("ACE rule={} translated to table={}.", ace, request); + return request; + } + + @Override + public ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, + @Nonnull final AceIpAndEth ace, + @Nullable final InterfaceMode mode, final int tableIndex, + final int vlanTags) { + final ClassifyAddDelSession request = createSession(action, tableIndex); + request.match = new byte[maskLength(ace)]; + + boolean noMatch = destinationMacAddressMatch(ace.getDestinationMacAddress(), request); + noMatch &= sourceMacAddressMatch(ace.getSourceMacAddress(), request); + + final AceIpVersion aceIpVersion = ace.getAceIpVersion(); + checkArgument(aceIpVersion != null, "AceIpAndEth have to define IpVersion"); + + final int baseOffset = getVlanTagsLen(vlanTags); + if (aceIpVersion instanceof AceIpv4) { + final AceIpv4 ipVersion = (AceIpv4) aceIpVersion; + noMatch &= ip4Match(baseOffset, mode, ace, ipVersion, request, LOG); + } else if (aceIpVersion instanceof AceIpv6) { + final AceIpv6 ipVersion = (AceIpv6) aceIpVersion; + noMatch &= ip6Match(baseOffset, mode, ace, ipVersion, request, LOG); + } else { + throw new IllegalArgumentException(String.format("Unsupported IP version %s", aceIpVersion)); + } + + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field match values", ace.toString())); + } + + LOG.debug("ACE action={}, rule={} translated to session={}.", action, ace, request); + return request; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java index 7ba44f166..790b6d664 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceWriter.java @@ -16,34 +16,38 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import io.fd.honeycomb.translate.write.WriteFailedException; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import java.util.List; -import javax.annotation.Nonnegative; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; import javax.annotation.Nonnull; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; +import javax.annotation.Nullable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; -import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; /** * Writer responsible for translation of ietf-acl model ACEs to VPP's classify tables and sessions. + * + * @param <T> type of access control list entry */ -interface AceWriter { +interface AceWriter<T extends AceType> { + /** + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param nextTableIndex index of the next classify table in chain + * @param vlanTags number of vlan tags + */ + @Nonnull + ClassifyAddDelTable createTable(@Nonnull final T ace, @Nullable final InterfaceMode mode, final int nextTableIndex, + final int vlanTags); /** - * Translates list of ACEs to chain of classify tables. Each ACE is translated into one classify table with single - * classify session. Also initializes input_acl_set_interface request message DTO with first classify table of the - * chain that was created. - * - * @param id uniquely identifies ietf-acl container - * @param aces list of access control entries - * @param mode interface mode (L2/L3) - * @param defaultAction to be taken when packet that does not match any of rules defined in - * @param request input_acl_set_interface request DTO + * @param action to be taken when packet does match the specified ace + * @param ace access list entry + * @param mode interface mode (L2/L3) + * @param tableIndex index of corresponding classify table + * @param vlanTags number of vlan tags */ - void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces, - final InterfaceMode mode, final AccessLists.DefaultAction defaultAction, - @Nonnull final InputAclSetInterface request, @Nonnegative final int vlanTags) - throws WriteFailedException; + @Nonnull + ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, @Nonnull T ace, + @Nullable final InterfaceMode mode, final int tableIndex, final int vlanTags); } diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java new file mode 100644 index 000000000..9a931a923 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AclTranslator.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.Permit; + +/** + * Utility that helps translating of ietf-acl model ACEs to VPP's classify tables and sessions. + */ +interface AclTranslator { + + // TODO: HONEYCOMB-181 minimise memory used by classify tables (we create a lot of them to make ietf-acl model + // mapping more convenient): + // according to https://wiki.fd.io/view/VPP/Introduction_To_N-tuple_Classifiers#Creating_a_classifier_table, + // classify table needs 16*(1 + match_n_vectors) bytes, but this does not quite work, so setting 8K for now + int TABLE_MEM_SIZE = 8 * 1024; + int VLAN_TAG_LEN = 4; + + default ClassifyAddDelTable createTable(final int nextTableIndex) { + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.isAdd = 1; + request.tableIndex = -1; // value not present + request.nbuckets = 1; // we expect exactly one session per table + request.nextTableIndex = nextTableIndex; + request.memorySize = TABLE_MEM_SIZE; + request.missNextIndex = -1; // value not set, but anyway it is ignored for tables in chain + return request; + } + + default ClassifyAddDelSession createSession(@Nonnull final PacketHandling action, final int tableIndex) { + final ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + request.opaqueIndex = ~0; // value not used + + if (action instanceof Permit) { + request.hitNextIndex = -1; + } // deny (0) is default value + + return request; + } + + default int getVlanTagsLen(final int vlanTags) { + return vlanTags * VLAN_TAG_LEN; + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java index 9074684d7..4eac0fad6 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizer.java @@ -41,10 +41,10 @@ import org.slf4j.LoggerFactory; public class IetfAclCustomizer implements WriterCustomizer<Ingress> { private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public IetfAclCustomizer(@Nonnull final IetfAClWriter aclWriter, + public IetfAclCustomizer(@Nonnull final IetfAclWriter aclWriter, @Nonnull final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java index 2fd00e39e..5814211f4 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAClWriter.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclWriter.java @@ -20,10 +20,11 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer; import io.fd.honeycomb.translate.write.WriteContext; import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSessionReply; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTableReply; import io.fd.vpp.jvpp.core.dto.ClassifyTableByInterface; @@ -33,8 +34,10 @@ import io.fd.vpp.jvpp.core.dto.InputAclSetInterfaceReply; import io.fd.vpp.jvpp.core.future.FutureJVppCore; import java.util.HashMap; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.concurrent.CompletionStage; +import java.util.function.Predicate; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.annotation.Nonnegative; @@ -44,30 +47,35 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.cont import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.AceIpVersion; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv4; -import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.ace.ip.version.AceIpv6; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ietf.acl.base.attributes.access.lists.Acl; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public final class IetfAClWriter implements JvppReplyConsumer { +public final class IetfAclWriter implements JvppReplyConsumer, AclTranslator { - private static final Logger LOG = LoggerFactory.getLogger(IetfAClWriter.class); + private static final Logger LOG = LoggerFactory.getLogger(IetfAclWriter.class); + private static final int NOT_DEFINED = -1; private final FutureJVppCore jvpp; - private Map<AclType, AceWriter> aceWriters = new HashMap<>(); + private Map<AclType, AceWriter<? extends AceType>> aceWriters = new HashMap<>(); - public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) { + public IetfAclWriter(@Nonnull final FutureJVppCore futureJVppCore) { this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null"); - aceWriters.put(AclType.ETH, new AceEthWriter(futureJVppCore)); - aceWriters.put(AclType.IP4, new AceIp4Writer(futureJVppCore)); - aceWriters.put(AclType.IP6, new AceIp6Writer(futureJVppCore)); + aceWriters.put(AclType.ETH, new AceEthWriter()); + aceWriters.put(AclType.IP4, new AceIp4Writer()); + aceWriters.put(AclType.IP6, new AceIp6Writer()); + aceWriters.put(AclType.ETH_AND_IP, new AceIpAndEthWriter()); } private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl, @@ -77,12 +85,12 @@ public final class IetfAClWriter implements JvppReplyConsumer { // ietf-acl updates are handled first, so we use writeContext.readAfter final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl> - aclOptional = writeContext.readAfter(IetfAclWriter.ACL_ID.child( - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, - new AclKey(aclName, aclType))); + aclOptional = writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, + new AclKey(aclName, aclType))); checkArgument(aclOptional.isPresent(), "Acl lists not configured"); final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl - acl = aclOptional.get(); + acl = aclOptional.get(); final AccessListEntries accessListEntries = acl.getAccessListEntries(); checkArgument(accessListEntries != null, "access list entries not configured"); @@ -91,7 +99,7 @@ public final class IetfAClWriter implements JvppReplyConsumer { } void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex) - throws WriteFailedException { + throws WriteFailedException { final ClassifyTableByInterface request = new ClassifyTableByInterface(); request.swIfIndex = swIfIndex; @@ -110,7 +118,7 @@ public final class IetfAClWriter implements JvppReplyConsumer { private void unassignClassifyTables(@Nonnull final InstanceIdentifier<?> id, final ClassifyTableByInterfaceReply currentState) - throws WriteFailedException { + throws WriteFailedException { final InputAclSetInterface request = new InputAclSetInterface(); request.isAdd = 0; request.swIfIndex = currentState.swIfIndex; @@ -118,12 +126,12 @@ public final class IetfAClWriter implements JvppReplyConsumer { request.ip4TableIndex = currentState.ip4TableId; request.ip6TableIndex = currentState.ip6TableId; final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = - jvpp.inputAclSetInterface(request); + jvpp.inputAclSetInterface(request); getReplyForDelete(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); } private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex) - throws WriteFailedException { + throws WriteFailedException { if (tableIndex == -1) { return; // classify table id is absent @@ -135,49 +143,138 @@ public final class IetfAClWriter implements JvppReplyConsumer { } void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls, - final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, @Nonnull final WriteContext writeContext) - throws WriteFailedException { + final AccessLists.DefaultAction defaultAction, @Nullable final InterfaceMode mode, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { write(id, swIfIndex, mode, acls, defaultAction, writeContext, 0); } + private static boolean appliesToIp4Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv4) { + return true; + } + if (aceType instanceof AceEth) { + return true; // L2 only rules are possible for IP4 traffic + } + if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4) { + return true; + } + return false; + } + + private static boolean appliesToIp6Path(final Ace ace) { + final AceType aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceIp && ((AceIp) aceType).getAceIpVersion() instanceof AceIpv6) { + return true; + } + if (aceType instanceof AceEth) { + return true; // L2 only rules are possible for IP6 traffic + } + if (aceType instanceof AceIpAndEth && ((AceIpAndEth)aceType).getAceIpVersion() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv6) { + return true; + } + return false; + } + void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, final InterfaceMode mode, @Nonnull final List<Acl> acls, final AccessLists.DefaultAction defaultAction, @Nonnull final WriteContext writeContext, @Nonnegative final int numberOfTags) - throws WriteFailedException { - - // filter ACE entries and group by AceType - final Map<AclType, List<Ace>> acesByType = acls.stream() - .flatMap(acl -> aclToAceStream(acl, writeContext)) - .collect(Collectors.groupingBy(AclType::fromAce)); + throws WriteFailedException { + checkArgument(numberOfTags >= 0 && numberOfTags <= 2, "Number of vlan tags %s is not in [0,2] range"); final InputAclSetInterface request = new InputAclSetInterface(); request.isAdd = 1; request.swIfIndex = swIfIndex; - request.l2TableIndex = -1; - request.ip4TableIndex = -1; - request.ip6TableIndex = -1; + request.l2TableIndex = NOT_DEFINED; + request.ip4TableIndex = NOT_DEFINED; + request.ip6TableIndex = NOT_DEFINED; + + if (InterfaceMode.L2.equals(mode)) { + final List<Ace> aces = getACEs(acls, writeContext, ace -> true); + request.l2TableIndex = writeAces(id, aces, defaultAction, mode, numberOfTags); + } else { + final List<Ace> ip4Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp4Path)); + request.ip4TableIndex = writeAces(id, ip4Aces, defaultAction, mode, numberOfTags); + final List<Ace> ip6Aces = getACEs(acls, writeContext, (IetfAclWriter::appliesToIp6Path)); + request.ip6TableIndex = writeAces(id, ip6Aces, defaultAction, mode, numberOfTags); + } + + final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = + jvpp.inputAclSetInterface(request); + getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + } - // for each AceType: - for (Map.Entry<AclType, List<Ace>> entry : acesByType.entrySet()) { - final AclType aceType = entry.getKey(); - final List<Ace> aces = entry.getValue(); - LOG.trace("Processing ACEs of {} type: {}", aceType, aces); + private static List<Ace> getACEs(@Nonnull final List<Acl> acls, @Nonnull final WriteContext writeContext, + final Predicate<? super Ace> filter) { + return acls.stream().flatMap(acl -> aclToAceStream(acl, writeContext)).filter(filter) + .collect(Collectors.toList()); + } + + private int writeAces(final InstanceIdentifier<?> id, final List<Ace> aces, + final AccessLists.DefaultAction defaultAction, final InterfaceMode mode, + final int vlanTags) throws WriteFailedException { + if (aces.isEmpty()) { + return NOT_DEFINED; + } - final AceWriter aceWriter = aceWriters.get(aceType); + int nextTableIndex = configureDefaultAction(id, defaultAction); + final ListIterator<Ace> iterator = aces.listIterator(aces.size()); + while (iterator.hasPrevious()) { + final Ace ace = iterator.previous(); + LOG.trace("Processing ACE: {}", ace); + + final AceWriter aceWriter = + aceWriters.get(AclType.fromAce(ace)); if (aceWriter == null) { - LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType); + LOG.warn("AceProcessor for {} not registered. Skipping ACE.", ace.getClass()); } else { - aceWriter.write(id, aces, mode, defaultAction, request, numberOfTags); + final AceType aceType = ace.getMatches().getAceType(); + final PacketHandling action = ace.getActions().getPacketHandling(); + final ClassifyAddDelTable ctRequest = aceWriter.createTable(aceType, mode, nextTableIndex, vlanTags); + nextTableIndex = createClassifyTable(id, ctRequest); + final ClassifyAddDelSession csRequest = + aceWriter.createSession(action, aceType, mode, nextTableIndex, vlanTags); + createClassifySession(id, csRequest); } } + return nextTableIndex; + } - final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = - jvpp.inputAclSetInterface(request); - getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + private int configureDefaultAction(@Nonnull final InstanceIdentifier<?> id, + final AccessLists.DefaultAction defaultAction) + throws WriteFailedException { + ClassifyAddDelTable ctRequest = createTable(-1); + if (AccessLists.DefaultAction.Permit.equals(defaultAction)) { + ctRequest.missNextIndex = -1; + } else { + ctRequest.missNextIndex = 0; + } + ctRequest.mask = new byte[16]; + ctRequest.skipNVectors = 0; + ctRequest.matchNVectors = 1; + return createClassifyTable(id, ctRequest); + } + + private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelTable request) + throws WriteFailedException { + final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request); + + final ClassifyAddDelTableReply reply = getReplyForWrite(cs.toCompletableFuture(), id); + return reply.newTableIndex; + } + + private void createClassifySession(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelSession request) + throws WriteFailedException { + final CompletionStage<ClassifyAddDelSessionReply> cs = jvpp.classifyAddDelSession(request); + + getReplyForWrite(cs.toCompletableFuture(), id); } private enum AclType { - ETH, IP4, IP6; + ETH, IP4, IP6, ETH_AND_IP; @Nonnull private static AclType fromAce(final Ace ace) { @@ -194,6 +291,8 @@ public final class IetfAClWriter implements JvppReplyConsumer { } else { result = IP6; } + } else if (aceType instanceof AceIpAndEth) { + result = ETH_AND_IP; } } catch (NullPointerException e) { throw new IllegalArgumentException("Incomplete ACE: " + ace, e); diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java new file mode 100644 index 000000000..819561e78 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip4AclTranslator.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import com.google.common.primitives.Ints; +import io.fd.honeycomb.translate.vpp.util.Ipv4Translator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv4HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; + +public interface Ip4AclTranslator extends Ipv4Translator { + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int DSCP_OFFSET = 15; + int DSCP_MASK = 0xfc; + + int IP_PROTOCOL_OFFSET = ETHER_TYPE_OFFSET + 11; + int IP_PROTOCOL_MASK = 0xff; + + int IP4_LEN = 4; + int IP4_MASK_BIT_LENGTH = 32; + int SRC_IP_OFFSET = ETHER_TYPE_OFFSET + 14; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP4_LEN; + + default boolean ip4Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final ClassifyAddDelTable request, final Logger log) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + request.mask[baseOffset + DSCP_OFFSET] = (byte) DSCP_MASK; // first 6 bits + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip4.getSourceIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getSourceIpv4Network()), 0, request.mask, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + } + if (ip4.getDestinationIpv4Network() != null) { + aceIsEmpty = false; + System.arraycopy(Impl.toByteMask(ip4.getDestinationIpv4Network()), 0, request.mask, + baseOffset + DST_IP_OFFSET, IP4_LEN); + } + return aceIsEmpty; + } + + default boolean ip4Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv4HeaderFields ip4, final ClassifyAddDelSession request, final Logger log) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP4 etherType (0x0800) + request.match[baseOffset + ETHER_TYPE_OFFSET] = 0x08; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = 0x00; + } + if (header.getDscp() != null) { + noMatch = false; + request.match[baseOffset + DSCP_OFFSET] = (byte) (DSCP_MASK & (header.getDscp().getValue() << 2)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip4.getSourceIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getSourceIpv4Network()), 0, request.match, + baseOffset + SRC_IP_OFFSET, IP4_LEN); + + } + if (ip4.getDestinationIpv4Network() != null) { + noMatch = false; + System.arraycopy(Impl.toMatchValue(ip4.getDestinationIpv4Network()), 0, request.match, + baseOffset + DST_IP_OFFSET, IP4_LEN); + + } + return noMatch; + } + + class Impl { + private static byte[] toByteMask(final int prefixLength) { + final long mask = ((1L << prefixLength) - 1) << (IP4_MASK_BIT_LENGTH - prefixLength); + return Ints.toByteArray((int) mask); + } + + private static byte[] toByteMask(final Ipv4Prefix ipv4Prefix) { + final int prefixLength = Byte.valueOf(ipv4Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv4Prefix ipv4Prefix) { + final String[] split = ipv4Prefix.getValue().split("/"); + final byte[] addressBytes = Ipv4Translator.INSTANCE.ipv4AddressNoZoneToArray(split[0]); + final byte[] mask = Impl.toByteMask(Byte.valueOf(split[1])); + for (int i = 0; i < addressBytes.length; ++i) { + addressBytes[i] &= mask[i]; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java new file mode 100644 index 000000000..eb2ec8c10 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/Ip6AclTranslator.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.BitSet; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpHeaderFields; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.AclIpv6HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.slf4j.Logger; + +public interface Ip6AclTranslator { + + int ETHER_TYPE_OFFSET = 12; // first 14 bytes represent L2 header (2x6) + int IP_VERSION_OFFSET = ETHER_TYPE_OFFSET + 2; + int DSCP_MASK1 = 0x0f; + int DSCP_MASK2 = 0xc0; + int IP_PROTOCOL_OFFSET = IP_VERSION_OFFSET + 6; + int IP_PROTOCOL_MASK = 0xff; + int IP6_LEN = 16; + int SRC_IP_OFFSET = IP_VERSION_OFFSET + 8; + int DST_IP_OFFSET = SRC_IP_OFFSET + IP6_LEN; + + default boolean ip6Mask(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final ClassifyAddDelTable request, final Logger log) { + boolean aceIsEmpty = true; + if (InterfaceMode.L2.equals(mode)) { + // in L2 mode we need to match ether type + request.mask[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0xff; + request.mask[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xff; + } + if (header.getDscp() != null) { + aceIsEmpty = false; + // DCSP (bits 4-9 of IP6 header) + request.mask[baseOffset + IP_VERSION_OFFSET] |= DSCP_MASK1; + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= DSCP_MASK2; + } + if (header.getProtocol() != null) { // Internet Protocol number + aceIsEmpty = false; + request.mask[baseOffset + IP_PROTOCOL_OFFSET] = (byte) IP_PROTOCOL_MASK; + } + if (ip6.getFlowLabel() != null) { + aceIsEmpty = false; + // bits 12-31 + request.mask[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) 0x0f; + request.mask[baseOffset + IP_VERSION_OFFSET + 2] = (byte) 0xff; + request.mask[baseOffset + IP_VERSION_OFFSET + 3] = (byte) 0xff; + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip6.getSourceIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getSourceIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + SRC_IP_OFFSET, mask.length); + } + if (ip6.getDestinationIpv6Network() != null) { + aceIsEmpty = false; + final byte[] mask = Impl.toByteMask(ip6.getDestinationIpv6Network()); + System.arraycopy(mask, 0, request.mask, baseOffset + DST_IP_OFFSET, mask.length); + } + return aceIsEmpty; + } + + default boolean ip6Match(final int baseOffset, final InterfaceMode mode, final AclIpHeaderFields header, + final AclIpv6HeaderFields ip6, final ClassifyAddDelSession request, final Logger log) { + boolean noMatch = true; + if (InterfaceMode.L2.equals(mode)) { + // match IP6 etherType (0x86dd) + request.match[baseOffset + ETHER_TYPE_OFFSET] = (byte) 0x86; + request.match[baseOffset + ETHER_TYPE_OFFSET + 1] = (byte) 0xdd; + } + if (header.getDscp() != null) { + noMatch = false; + final int dcsp = header.getDscp().getValue(); + // set bits 4-9 of IP6 header: + request.match[baseOffset + IP_VERSION_OFFSET] |= (byte) (DSCP_MASK1 & (dcsp >> 2)); + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (DSCP_MASK2 & (dcsp << 6)); + } + if (header.getProtocol() != null) { // Internet Protocol number + noMatch = false; + request.match[baseOffset + IP_PROTOCOL_OFFSET] = (byte) (IP_PROTOCOL_MASK & header.getProtocol()); + } + if (ip6.getFlowLabel() != null) { + noMatch = false; + final int flowLabel = ip6.getFlowLabel().getValue().intValue(); + // bits 12-31 + request.match[baseOffset + IP_VERSION_OFFSET + 1] |= (byte) (0x0f & (flowLabel >> 16)); + request.match[baseOffset + IP_VERSION_OFFSET + 2] = (byte) (0xff & (flowLabel >> 8)); + request.match[baseOffset + IP_VERSION_OFFSET + 3] = (byte) (0xff & flowLabel); + } + if (header.getSourcePortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getSourcePortRange()); + } + if (header.getDestinationPortRange() != null) { + log.warn("L4 Header fields are not supported. Ignoring {}", header.getDestinationPortRange()); + } + if (ip6.getSourceIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getSourceIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + SRC_IP_OFFSET, IP6_LEN); + } + if (ip6.getDestinationIpv6Network() != null) { + noMatch = false; + final byte[] match = Impl.toMatchValue(ip6.getDestinationIpv6Network()); + System.arraycopy(match, 0, request.match, baseOffset + DST_IP_OFFSET, IP6_LEN); + } + return noMatch; + } + + class Impl { + private static final int IP6_MASK_BIT_LENGTH = 128; + + private static byte[] toByteMask(final int prefixLength) { + final BitSet mask = new BitSet(IP6_MASK_BIT_LENGTH); + mask.set(0, prefixLength, true); + if (prefixLength < IP6_MASK_BIT_LENGTH) { + mask.set(prefixLength, IP6_MASK_BIT_LENGTH, false); + } + return mask.toByteArray(); + } + + private static byte[] toByteMask(final Ipv6Prefix ipv6Prefix) { + final int prefixLength = Short.valueOf(ipv6Prefix.getValue().split("/")[1]); + return toByteMask(prefixLength); + } + + private static byte[] toMatchValue(final Ipv6Prefix ipv6Prefix) { + final String[] split = ipv6Prefix.getValue().split("/"); + final byte[] addressBytes; + try { + addressBytes = InetAddress.getByName(split[0]).getAddress(); + } catch (UnknownHostException e) { + throw new IllegalArgumentException("Invalid IP6 address", e); + } + final byte[] mask = toByteMask(Short.valueOf(split[1])); + int pos = 0; + for (; pos < mask.length; ++pos) { + addressBytes[pos] &= mask[pos]; + } + // mask can be shorter that address, so we need to clear rest of the address: + for (; pos < addressBytes.length; ++pos) { + addressBytes[pos] = 0; + } + return addressBytes; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java new file mode 100644 index 000000000..a802ed3fa --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/L2AclTranslator.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import io.fd.honeycomb.translate.vpp.util.MacTranslator; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import java.util.List; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; + +public interface L2AclTranslator extends MacTranslator { + + default boolean destinationMacAddressMask(final MacAddress dstMask, final MacAddress dstAddress, + final ClassifyAddDelTable request) { + // destination-mac-address or destination-mac-address-mask is present => + // ff:ff:ff:ff:ff:ff:00:00:00:00:00:00:00:00:00:00 + if (dstMask != null) { + final List<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/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java index fe4e22142..fdefc0855 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizer.java @@ -48,10 +48,10 @@ import org.slf4j.LoggerFactory; public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<Ingress> { private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); - private final IetfAClWriter aclWriter; + private final IetfAclWriter aclWriter; private final NamingContext interfaceContext; - public SubInterfaceIetfAclCustomizer(@Nonnull final IetfAClWriter aclWriter, + public SubInterfaceIetfAclCustomizer(@Nonnull final IetfAclWriter aclWriter, @Nonnull final NamingContext interfaceContext) { this.aclWriter = checkNotNull(aclWriter, "aclWriter should not be null"); this.interfaceContext = checkNotNull(interfaceContext, "interfaceContext should not be null"); |