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 | |
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>
29 files changed, 984 insertions, 777 deletions
diff --git a/v3po/api/src/main/yang/v3po.yang b/v3po/api/src/main/yang/v3po.yang index b2d0a74ed..a5ccec5f9 100644 --- a/v3po/api/src/main/yang/v3po.yang +++ b/v3po/api/src/main/yang/v3po.yang @@ -7,7 +7,7 @@ module v3po { description "This revision adds the following new features: - ingress/egress ACLs support - - default-action and interface-mode type as a part of ietf-acl configuration"; + - moved ACL definitions to vpp-acl module"; } revision "2015-01-05" { diff --git a/v3po/api/src/main/yang/vpp-acl.yang b/v3po/api/src/main/yang/vpp-acl.yang index 76e1eda9e..d0d24c98a 100644 --- a/v3po/api/src/main/yang/vpp-acl.yang +++ b/v3po/api/src/main/yang/vpp-acl.yang @@ -70,11 +70,13 @@ module vpp-acl { container access-lists { description - "Defines references to ietf-acl lists. Before assignment to interface, - ACL lists are merged into 3 type of acls (l2, ip4 and ip6) that are supported by vpp. - Then 3 corresponding chains of tables and sessions are created and assigned to the interface - as l2, ip4 and ip6 classify table chains. - User ordering is preserved in each group separately. + "Defines references to ietf-acl lists. + ACLs are translated into classify tables and sessions when assigned to interface. + + In case of L2 interfaces, acls are translated into a chain of classify tables and assigned as L2 table. + In case of L3 interfaces, acls are translated into ip4 and ip6 chains (eth only rules go to both chains, + rest - depending on ip-version). + User ordering is preserved in both cases. Assignment update/delete removes all created tables and sessions and repeats process described above. Update/delete of ACL lists referenced here is not permitted (assignment needs to be removed first). @@ -85,10 +87,11 @@ module vpp-acl { Limitations (due to vpp limitations): - egress rules are currently ignored (HONEYCOMB-234) - L4 rules are currently not supported (limited support will by provided by HONEYCOMB-218) - - mixing L2/L3/L4 rules is currently not supported (limited support will by provided by HONEYCOMB-233) - - L2 only rules on L3 interfaces are not supported (not allowed by vpp, - in the future defining L2/L3 pairs should be partially supported) + - mixing L2 and L3 rules is possible only if ace-ip-version is provided + (vpp classfier api limitation: common header fields for IP4/IP6 have different offsets) + - L2 rules on L3 interfaces only to IP traffic (vpp classfier limitation) - vlan tags are supported only for sub-interfaces defined as exact-match"; + list acl { key "type name"; ordered-by user; diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java index 8e001b75d..301c30501 100644 --- a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java @@ -18,10 +18,10 @@ package io.fd.honeycomb.translate.v3po; import com.google.inject.Inject; import com.google.inject.Provider; -import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.IetfAclWriter; import io.fd.vpp.jvpp.core.future.FutureJVppCore; -class IetfAClWriterProvider implements Provider<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"); diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java index 1a89a5d98..546de89d7 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceEthWriterTest.java @@ -22,11 +22,8 @@ import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceEth; @@ -36,8 +33,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceEthWriterTest { - @Mock - private FutureJVppCore jvpp; private AceEthWriter writer; private PacketHandling action; private AceEth aceEth; @@ -45,7 +40,7 @@ public class AceEthWriterTest { @Before public void setUp() { initMocks(this); - writer = new AceEthWriter(jvpp); + writer = new AceEthWriter(); action = new DenyBuilder().setDeny(true).build(); aceEth = new AceEthBuilder() .setDestinationMacAddress(new MacAddress("11:22:33:44:55:66")) @@ -56,9 +51,9 @@ public class AceEthWriterTest { } @Test - public void testCreateClassifyTable() { + public void testCreateTable() { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceEth, InterfaceMode.L2, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceEth, InterfaceMode.L2, nextTableIndex, 0); assertEquals(1, request.isAdd); assertEquals(-1, request.tableIndex); @@ -78,15 +73,10 @@ public class AceEthWriterTest { assertArrayEquals(expectedMask, request.mask); } - @Test(expected = IllegalArgumentException.class) - public void testCreateClassifyTableForL3Interface() { - writer.createClassifyTable(aceEth, InterfaceMode.L3, 42, 0); - } - @Test public void testCreateClassifySession() { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceEth, InterfaceMode.L2, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceEth, InterfaceMode.L2, tableIndex, 0); assertEquals(1, request.isAdd); assertEquals(tableIndex, request.tableIndex); @@ -101,17 +91,4 @@ public class AceEthWriterTest { }; assertArrayEquals(expectedMatch, request.match); } - - @Test(expected = IllegalArgumentException.class) - public void testCreateClassifySessionForL3Interface() { - writer.createClassifySession(action, aceEth, InterfaceMode.L3, 42, 0); - } - - @Test - public void testSetClassifyTable() { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.l2TableIndex); - } }
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java index 1a7045529..258a937ac 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp4WriterTest.java @@ -16,17 +16,14 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclTranslator.VLAN_TAG_LEN; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; @@ -38,8 +35,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceIp4WriterTest { - @Mock - private FutureJVppCore jvpp; private AceIp4Writer writer; private PacketHandling action; private AceIp aceIp; @@ -47,7 +42,7 @@ public class AceIp4WriterTest { @Before public void setUp() throws Exception { initMocks(this); - writer = new AceIp4Writer(jvpp); + writer = new AceIp4Writer(); action = new DenyBuilder().setDeny(true).build(); aceIp = new AceIpBuilder() .setProtocol((short) 132) @@ -120,46 +115,46 @@ public class AceIp4WriterTest { } @Test - public void testCreateClassifyTable() throws Exception { + public void testCreateTable() throws Exception { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, false); } @Test - public void testCreateClassifyTableForL2Interface() throws Exception { + public void testCreateTableForL2Interface() throws Exception { final int nextTableIndex = 42; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, true); } @Test - public void testCreateClassifyTable1VlanTag() throws Exception { + public void testCreateTable1VlanTag() throws Exception { final int nextTableIndex = 42; final int vlanTags = 1; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test - public void testCreateClassifyTable2VlanTags() throws Exception { + public void testCreateTable2VlanTags() throws Exception { final int nextTableIndex = 42; final int vlanTags = 2; - final ClassifyAddDelTable request = writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + final ClassifyAddDelTable request = writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test public void testCreateClassifySession() throws Exception { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, false); } @Test public void testCreateClassifySessionForL2Interface() throws Exception { final int tableIndex = 123; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L2, tableIndex, 0); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L2, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, true); } @@ -167,7 +162,7 @@ public class AceIp4WriterTest { public void testCreateClassifySession1VlanTag() throws Exception { final int tableIndex = 123; final int vlanTags = 1; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } @@ -175,16 +170,8 @@ public class AceIp4WriterTest { public void testCreateClassifySession2VlanTags() throws Exception { final int tableIndex = 123; final int vlanTags = 2; - final ClassifyAddDelSession request = writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + final ClassifyAddDelSession request = writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } - - @Test - public void testSetClassifyTable() throws Exception { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.ip4TableIndex); - } }
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java index 01eaa454d..08f90557e 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIp6WriterTest.java @@ -16,17 +16,14 @@ package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; -import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AbstractAceWriter.VLAN_TAG_LEN; +import static io.fd.honeycomb.translate.v3po.interfaces.acl.ingress.AclTranslator.VLAN_TAG_LEN; import static org.junit.Assert.assertEquals; import static org.mockito.MockitoAnnotations.initMocks; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; -import io.fd.vpp.jvpp.core.dto.InputAclSetInterface; -import io.fd.vpp.jvpp.core.future.FutureJVppCore; import org.junit.Before; import org.junit.Test; -import org.mockito.Mock; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIp; @@ -39,8 +36,6 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl. public class AceIp6WriterTest { - @Mock - private FutureJVppCore jvpp; private AceIp6Writer writer; private PacketHandling action; private AceIp aceIp; @@ -48,7 +43,7 @@ public class AceIp6WriterTest { @Before public void setUp() { initMocks(this); - writer = new AceIp6Writer(jvpp); + writer = new AceIp6Writer(); action = new DenyBuilder().setDeny(true).build(); aceIp = new AceIpBuilder() .setProtocol((short) 132) @@ -128,36 +123,36 @@ public class AceIp6WriterTest { } @Test - public void testCreateClassifyTable() { + public void testCreateTable() { final int nextTableIndex = 42; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, false); } @Test - public void testCreateClassifyTableForL2Interface() { + public void testCreateTableForL2Interface() { final int nextTableIndex = 42; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); + writer.createTable(aceIp, InterfaceMode.L2, nextTableIndex, 0); verifyTableRequest(request, nextTableIndex, 0, true); } @Test - public void testCreateClassifyTable1VlanTag() { + public void testCreateTable1VlanTag() { final int nextTableIndex = 42; final int vlanTags = 1; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @Test - public void testCreateClassifyTable2VlanTags() { + public void testCreateTable2VlanTags() { final int nextTableIndex = 42; final int vlanTags = 2; final ClassifyAddDelTable request = - writer.createClassifyTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); + writer.createTable(aceIp, InterfaceMode.L3, nextTableIndex, vlanTags); verifyTableRequest(request, nextTableIndex, vlanTags, false); } @@ -165,7 +160,7 @@ public class AceIp6WriterTest { public void testCreateClassifySession() { final int tableIndex = 123; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, 0); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, false); } @@ -173,7 +168,7 @@ public class AceIp6WriterTest { public void testCreateClassifySessionForL2Interface() { final int tableIndex = 123; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L2, tableIndex, 0); + writer.createSession(action, aceIp, InterfaceMode.L2, tableIndex, 0); verifySessionRequest(request, tableIndex, 0, true); } @@ -182,7 +177,7 @@ public class AceIp6WriterTest { final int tableIndex = 123; final int vlanTags = 1; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } @@ -191,15 +186,7 @@ public class AceIp6WriterTest { final int tableIndex = 123; final int vlanTags = 2; final ClassifyAddDelSession request = - writer.createClassifySession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); + writer.createSession(action, aceIp, InterfaceMode.L3, tableIndex, vlanTags); verifySessionRequest(request, tableIndex, vlanTags, false); } - - @Test - public void testSetClassifyTable() { - final int tableIndex = 321; - final InputAclSetInterface request = new InputAclSetInterface(); - writer.setClassifyTable(request, tableIndex); - assertEquals(tableIndex, request.ip6TableIndex); - } }
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java new file mode 100644 index 000000000..1da23072b --- /dev/null +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/AceIpAndEthWriterTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po.interfaces.acl.ingress; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelSession; +import io.fd.vpp.jvpp.core.dto.ClassifyAddDelTable; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.PacketHandling; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.actions.packet.handling.DenyBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.InterfaceMode; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEth; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.AceIpAndEthBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.ace.ip.and.eth.ace.ip.version.AceIpv4Builder; + +public class AceIpAndEthWriterTest { + + private AceIpAndEthWriter writer; + private PacketHandling action; + private AceIpAndEth ace; + + @Before + public void setUp() { + initMocks(this); + writer = new AceIpAndEthWriter(); + action = new DenyBuilder().setDeny(true).build(); + ace = new AceIpAndEthBuilder() + .setDestinationMacAddress(new MacAddress("11:22:33:44:55:66")) + .setSourceMacAddress(new MacAddress("aa:bb:cc:dd:ee:ff")) + .setAceIpVersion(new AceIpv4Builder() + .setSourceIpv4Network(new Ipv4Prefix("1.2.3.4/32")).build()) + .build(); + } + + @Test + public void testCreateTable() { + final int nextTableIndex = 42; + final ClassifyAddDelTable request = writer.createTable(ace, InterfaceMode.L2, nextTableIndex, 0); + + assertEquals(1, request.isAdd); + assertEquals(-1, request.tableIndex); + assertEquals(1, request.nbuckets); + assertEquals(nextTableIndex, request.nextTableIndex); + assertEquals(0, request.skipNVectors); + assertEquals(3, request.matchNVectors); + assertEquals(AceEthWriter.TABLE_MEM_SIZE, request.memorySize); + + byte[] expectedMask = new byte[] { + // destination MAC: + -1, -1, -1, -1, -1, -1, + // source MAC: + -1, -1, -1, -1, -1, -1, + // ether type: + -1, -1, + // IP header + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // source address: + -1, -1, -1, -1, + // destination address: + 0, 0, 0, 0, + // padding: + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assertArrayEquals(expectedMask, request.mask); + } + + @Test + public void testCreateClassifySession() { + final int tableIndex = 123; + final ClassifyAddDelSession request = writer.createSession(action, ace, InterfaceMode.L2, tableIndex, 0); + + assertEquals(1, request.isAdd); + assertEquals(tableIndex, request.tableIndex); + assertEquals(0, request.hitNextIndex); + + byte[] expectedMatch = new byte[] { + // destination MAC: + (byte) 0x11, (byte) 0x22, (byte) 0x33, (byte) 0x44, (byte) 0x55, (byte) 0x66, + // source MAC: + (byte) 0xaa, (byte) 0xbb, (byte) 0xcc, (byte) 0xdd, (byte) 0xee, (byte) 0xff, + // ether type (IP4): + 0x08, 0, + // IP header + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + // source address: + 1, 2, 3, 4, + // destination address: + 0, 0, 0, 0, + // padding: + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + assertArrayEquals(expectedMatch, request.match); + } +}
\ No newline at end of file diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java index 9e010d0f8..6c7eea8b1 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/IetfAclCustomizerTest.java @@ -84,7 +84,7 @@ public class IetfAclCustomizerTest extends WriterCustomizerTest { @Override protected void setUp() { - customizer = new IetfAclCustomizer(new IetfAClWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); + customizer = new IetfAclCustomizer(new IetfAclWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); defineMapping(mappingContext, IF_NAME, IF_INDEX, IFC_TEST_INSTANCE); acl = new IngressBuilder().setAccessLists( new AccessListsBuilder().setAcl( diff --git a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java index 53b7ccf7e..76c681ca9 100644 --- a/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java +++ b/v3po/v3po2vpp/src/test/java/io/fd/honeycomb/translate/v3po/interfaces/acl/ingress/SubInterfaceIetfAclCustomizerTest.java @@ -21,7 +21,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.google.common.base.Optional; -import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter; import io.fd.honeycomb.translate.vpp.util.NamingContext; import io.fd.honeycomb.translate.write.WriteFailedException; import io.fd.honeycomb.vpp.test.write.WriterCustomizerTest; @@ -71,7 +70,7 @@ public class SubInterfaceIetfAclCustomizerTest extends WriterCustomizerTest { @Override protected void setUp() { customizer = - new SubInterfaceIetfAclCustomizer(new IetfAClWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); + new SubInterfaceIetfAclCustomizer(new IetfAclWriter(api), new NamingContext("prefix", IFC_TEST_INSTANCE)); defineMapping(mappingContext, IF_NAME, IF_INDEX, IFC_TEST_INSTANCE); acl = new IngressBuilder().setAccessLists( @@ -134,7 +133,7 @@ public class SubInterfaceIetfAclCustomizerTest extends WriterCustomizerTest { new SubInterfaceBuilder().build() )); - when(writeContext.readAfter(IetfAclWriter.ACL_ID.child( + when(writeContext.readAfter(io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclWriter.ACL_ID.child( org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, new AclKey(ACL_NAME, ACL_TYPE)))).thenReturn(Optional.of( new org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclBuilder() diff --git a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java index 84b0a2e68..56aa4f149 100644 --- a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java +++ b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/Ipv4Translator.java @@ -30,6 +30,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types. */ public interface Ipv4Translator extends ByteDataTranslator { + Ipv4Translator INSTANCE = new Ipv4Translator() {}; + /** * Creates address array from address part of {@link Ipv4Prefix} */ diff --git a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/MacTranslator.java b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/MacTranslator.java index a947f95c6..14cc28909 100644 --- a/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/MacTranslator.java +++ b/vpp-common/vpp-translate-utils/src/main/java/io/fd/honeycomb/translate/vpp/util/MacTranslator.java @@ -28,7 +28,7 @@ import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Hex; /** - * Trait providing logic for translation of ipv4-related data + * Trait providing logic for translation of MAC address data */ public interface MacTranslator { |