diff options
author | Marek Gradzki <mgradzki@cisco.com> | 2016-08-24 16:52:03 +0200 |
---|---|---|
committer | Marek Gradzki <mgradzki@cisco.com> | 2016-08-26 12:39:27 +0200 |
commit | b257036c963d274e2bcee43adc309782e5ddeab5 (patch) | |
tree | 20286c6a0600439b21c85fde021d5fd3ae118d74 /v3po/v3po2vpp/src | |
parent | cefd2555dbf421a62269c393d0d9d28964f38e2b (diff) |
HONEYCOMB-139: ietf-acl translation layer. L2 ACL support
Change-Id: I2b7de991e8d49c20fce66a5f4b193d0060feae56
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
Diffstat (limited to 'v3po/v3po2vpp/src')
12 files changed, 923 insertions, 5 deletions
diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/AclWriterFactory.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/AclWriterFactory.java new file mode 100644 index 000000000..d0050ab62 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/AclWriterFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.honeycomb.translate.v3po; + +import static io.fd.honeycomb.translate.v3po.InterfacesWriterFactory.IETF_ACL_ID; +import static io.fd.honeycomb.translate.v3po.SubinterfaceAugmentationWriterFactory.SUBIF_IETF_ACL_ID; + +import com.google.common.collect.Sets; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.honeycomb.translate.v3po.acl.AclWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl; +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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Matches; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public final class AclWriterFactory implements WriterFactory { + + public static final InstanceIdentifier<Acl> ACL_ID = + InstanceIdentifier.create(AccessLists.class).child(Acl.class); + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + + final InstanceIdentifier<Acl> aclIdRelative = InstanceIdentifier.create(Acl.class); + + final InstanceIdentifier<Ace> aceId = aclIdRelative.child(AccessListEntries.class).child(Ace.class); + final InstanceIdentifier<Actions> actionsId = aceId.child(Actions.class); + final InstanceIdentifier<Matches> matchesId = aceId.child(Matches.class); + + registry.subtreeAddBefore(Sets.newHashSet(aceId, actionsId, matchesId), + new GenericListWriter<>(ACL_ID, new AclWriter()), + Sets.newHashSet(IETF_ACL_ID, SUBIF_IETF_ACL_ID)); + } +} 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 new file mode 100644 index 000000000..f64574aac --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/IetfAClWriterProvider.java @@ -0,0 +1,37 @@ +/* + * 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; + +import com.google.inject.Inject; +import com.google.inject.Provider; +import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAClWriter; +import org.openvpp.jvpp.core.future.FutureJVppCore; + +class IetfAClWriterProvider implements Provider<IetfAClWriter> { + + private final FutureJVppCore jvpp; + + @Inject + public IetfAClWriterProvider(final FutureJVppCore jvpp) { + this.jvpp = jvpp; + } + + @Override + 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 efc75c96a..3c5d341ba 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,6 +35,8 @@ import io.fd.honeycomb.translate.v3po.interfaces.TapCustomizer; 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.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.IetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4AddressCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4Customizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.Ipv4NeighbourCustomizer; @@ -54,9 +56,11 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.Ip4Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.Ip6Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.AccessLists; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Ethernet; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Gre; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.IetfAcl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.L2; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.ProxyArp; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.Routing; @@ -75,18 +79,22 @@ public final class InterfacesWriterFactory implements WriterFactory, AutoCloseab IFC_ID.augmentation(VppInterfaceAugmentation.class); public static final InstanceIdentifier<Acl> ACL_ID = VPP_IFC_AUG_ID.child(Acl.class); public static final InstanceIdentifier<L2> L2_ID = VPP_IFC_AUG_ID.child(L2.class); + public static final InstanceIdentifier<IetfAcl> IETF_ACL_ID = VPP_IFC_AUG_ID.child(IetfAcl.class); private final FutureJVppCore jvpp; + private final IetfAClWriter aclWriter; private final NamingContext bdContext; private final NamingContext ifcContext; private final NamingContext classifyTableContext; @Inject public InterfacesWriterFactory(final FutureJVppCore vppJvppIfcDependency, + final IetfAClWriter aclWriter, @Named("bridge-domain-context") final NamingContext bridgeDomainContextDependency, @Named("interface-context") final NamingContext interfaceContextDependency, @Named("classify-table-context") final NamingContext classifyTableContextDependency) { this.jvpp = vppJvppIfcDependency; + this.aclWriter = aclWriter; this.bdContext = bridgeDomainContextDependency; this.ifcContext = interfaceContextDependency; this.classifyTableContext = classifyTableContextDependency; @@ -102,7 +110,7 @@ public final class InterfacesWriterFactory implements WriterFactory, AutoCloseab // Interface1 (ietf-ip augmentation) addInterface1AugmentationWriters(IFC_ID, registry); // SubinterfaceAugmentation TODO make dedicated module for subIfc writer factory - new SubinterfaceAugmentationWriterFactory(jvpp, ifcContext, bdContext, classifyTableContext).init(registry); + new SubinterfaceAugmentationWriterFactory(jvpp, aclWriter, ifcContext, bdContext, classifyTableContext).init(registry); } private void addInterface1AugmentationWriters(final InstanceIdentifier<Interface> ifcId, @@ -168,10 +176,19 @@ public final class InterfacesWriterFactory implements WriterFactory, AutoCloseab // also handles L2Acl, Ip4Acl and Ip6Acl: final InstanceIdentifier<Acl> aclId = InstanceIdentifier.create(Acl.class); registry - .subtreeAddAfter( - Sets.newHashSet(aclId.child(L2Acl.class), aclId.child(Ip4Acl.class), aclId.child(Ip6Acl.class)), - new GenericWriter<>(ACL_ID, new AclCustomizer(jvpp, ifcContext, classifyTableContext)), - Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); + .subtreeAddAfter( + Sets.newHashSet(aclId.child(L2Acl.class), aclId.child(Ip4Acl.class), aclId.child(Ip6Acl.class)), + new GenericWriter<>(ACL_ID, new AclCustomizer(jvpp, ifcContext, classifyTableContext)), + Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); + + // IETF-ACL, also handles IetfAcl, AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsID = InstanceIdentifier.create(IetfAcl.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclListId = accessListsID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsID, aclListId), + new GenericWriter<>(IETF_ACL_ID, new IetfAclCustomizer(aclWriter, ifcContext))); } 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 488938169..85c867e9b 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,6 +26,8 @@ import io.fd.honeycomb.translate.v3po.interfaces.RewriteCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.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.IetfAClWriter; +import io.fd.honeycomb.translate.v3po.interfaces.acl.SubInterfaceIetfAclCustomizer; import io.fd.honeycomb.translate.v3po.interfaces.ip.SubInterfaceIpv4AddressCustomizer; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.write.WriterFactory; @@ -34,11 +36,13 @@ import org.opendaylight.yang.gen.v1.urn.ieee.params.xml.ns.yang.dot1q.types.rev1 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.Ip4Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.Ip6Acl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.acl.base.attributes.L2Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.AccessLists; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.SubinterfaceAugmentation; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.SubInterfaces; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterface; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.match.attributes.match.type.vlan.tagged.VlanTagged; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.IetfAcl; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.L2; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.Tags; @@ -53,6 +57,7 @@ import org.openvpp.jvpp.core.future.FutureJVppCore; public final class SubinterfaceAugmentationWriterFactory implements WriterFactory { private final FutureJVppCore jvpp; + private final IetfAClWriter aclWriter; private final NamingContext ifcContext; private final NamingContext bdContext; private final NamingContext classifyTableContext; @@ -64,10 +69,13 @@ public final class SubinterfaceAugmentationWriterFactory implements WriterFactor public static final InstanceIdentifier<L2> L2_ID = SUB_IFC_ID.child( L2.class); public static final InstanceIdentifier<Acl> SUBIF_ACL_ID = SUB_IFC_ID.child(Acl.class); + public static final InstanceIdentifier<IetfAcl> SUBIF_IETF_ACL_ID = SUB_IFC_ID.child(IetfAcl.class); public SubinterfaceAugmentationWriterFactory(final FutureJVppCore jvpp, + final IetfAClWriter aclWriter, final NamingContext ifcContext, final NamingContext bdContext, final NamingContext classifyTableContext) { this.jvpp = jvpp; + this.aclWriter = aclWriter; this.ifcContext = ifcContext; this.bdContext = bdContext; this.classifyTableContext = classifyTableContext; @@ -115,5 +123,13 @@ public final class SubinterfaceAugmentationWriterFactory implements WriterFactor new GenericWriter<>(SUBIF_ACL_ID, new SubInterfaceAclCustomizer(jvpp, ifcContext, classifyTableContext)), Sets.newHashSet(CLASSIFY_TABLE_ID, CLASSIFY_SESSION_ID)); + // IETF-ACL, also handles IetfAcl, AccessLists and Acl: + final InstanceIdentifier<AccessLists> accessListsID = InstanceIdentifier.create(IetfAcl.class) + .child(AccessLists.class); + final InstanceIdentifier<?> aclListId = accessListsID.child( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.access.lists.Acl.class); + registry.subtreeAdd( + Sets.newHashSet(accessListsID, aclListId), + new GenericWriter<>(SUBIF_IETF_ACL_ID, new SubInterfaceIetfAclCustomizer(aclWriter, ifcContext))); } } 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 4effb70e3..ada8f9d00 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,6 +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.IetfAClWriter; import io.fd.honeycomb.translate.v3po.notification.InterfaceChangeNotificationProducer; import io.fd.honeycomb.translate.v3po.util.NamingContext; import io.fd.honeycomb.translate.write.WriterFactory; @@ -54,6 +55,9 @@ public class V3poModule extends AbstractModule { // Executor needed for keepalives bind(ScheduledExecutorService.class).toInstance(Executors.newScheduledThreadPool(1)); + // Utils + bind(IetfAClWriter.class).toProvider(IetfAClWriterProvider.class); + // Readers final Multibinder<ReaderFactory> readerFactoryBinder = Multibinder.newSetBinder(binder(), ReaderFactory.class); readerFactoryBinder.addBinding().to(InterfacesStateReaderFactory.class); @@ -65,6 +69,7 @@ public class V3poModule extends AbstractModule { writerFactoryBinder.addBinding().to(InterfacesWriterFactory.class); writerFactoryBinder.addBinding().to(VppHoneycombWriterFactory.class); writerFactoryBinder.addBinding().to(VppClassifierHoneycombWriterFactory.class); + writerFactoryBinder.addBinding().to(AclWriterFactory.class); // Initializers final Multibinder<DataTreeInitializer> initializerBinder = diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/acl/AclWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/acl/AclWriter.java new file mode 100644 index 000000000..6c3b4efed --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/acl/AclWriter.java @@ -0,0 +1,105 @@ +/* + * 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.acl; + +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.Optional; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AccessLists; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.VppInterfaceAugmentation; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Writer customizer responsible for Access Control Lists management. Does not send any messages to VPP. All the config + * data are stored in HC and used when acl is assigned/unassigned to/from an interface. + * + * ACLs that are currently assigned to an interface cannot be updated/deleted. + */ +public class AclWriter implements ListWriterCustomizer<Acl, AclKey> { + + public static final InstanceIdentifier<AccessLists> ACL_ID = + InstanceIdentifier.create(AccessLists.class); + + private static final Logger LOG = LoggerFactory.getLogger(AclWriter.class); + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Creating ACL: iid={} dataAfter={}", id, dataAfter); + + // no vpp call, just updates DataTree + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("Updating ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter); + + if (isAssigned(dataAfter, writeContext)) { + throw new WriteFailedException(id, + String.format("Failed to update data at %s: acl %s is already assigned", id, dataAfter)); + } + + LOG.debug("Updating unassigned ACL: iid={} dataBefore={} dataAfter={}", id, dataBefore, dataAfter); + + // no vpp call, just updates DataTree + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Deleting ACL: iid={} dataBefore={}", id, dataBefore); + + if (isAssigned(dataBefore, writeContext)) { + throw new WriteFailedException(id, + String.format("Failed to delete data at %s: acl %s is already assigned", id, dataBefore)); + } + + LOG.debug("Deleting unassigned ACL: iid={} dataBefore={}", id, dataBefore); + + // no vpp call, just updates DataTree + } + + private static boolean isAssigned(@Nonnull final Acl acl, + @Nonnull final WriteContext writeContext) { + final String aclName = acl.getAclName(); + final Class<? extends AclBase> aclType = acl.getAclType(); + final Interfaces interfaces = writeContext.readAfter(InstanceIdentifier.create(Interfaces.class)).get(); + + return interfaces.getInterface().stream() + .map(i -> Optional.ofNullable(i.getAugmentation(VppInterfaceAugmentation.class)) + .map(aug -> aug.getIetfAcl()) + .map(ietfAcl -> ietfAcl.getAccessLists()) + .map(accessLists -> accessLists.getAcl()) + ) + .flatMap(iacl -> iacl.isPresent() + ? iacl.get().stream() + : Stream.empty()) + .filter(assignedAcl -> aclName.equals(assignedAcl.getName()) && aclType.equals(assignedAcl.getType())) + .findFirst().isPresent(); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java new file mode 100644 index 000000000..9f27271b5 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AbstractAceWriter.java @@ -0,0 +1,107 @@ +/* + * 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; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.util.RWUtils; +import io.fd.honeycomb.translate.v3po.util.TranslateUtils; +import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; +import java.util.List; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collector; +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 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.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.VppBaseCallException; +import org.openvpp.jvpp.core.dto.ClassifyAddDelSession; +import org.openvpp.jvpp.core.dto.ClassifyAddDelSessionReply; +import org.openvpp.jvpp.core.dto.ClassifyAddDelTable; +import org.openvpp.jvpp.core.dto.ClassifyAddDelTableReply; +import org.openvpp.jvpp.core.dto.InputAclSetInterface; +import org.openvpp.jvpp.core.future.FutureJVppCore; + +abstract class AbstractAceWriter<T extends AceType> implements AceWriter { + private static final Collector<PacketHandling, ?, PacketHandling> SINGLE_ITEM_COLLECTOR = + RWUtils.singleItemCollector(); + + private final FutureJVppCore futureJVppCore; + + public AbstractAceWriter(@Nonnull final FutureJVppCore futureJVppCore) { + this.futureJVppCore = checkNotNull(futureJVppCore, "futureJVppCore should not be null"); + } + + @Nonnull + public FutureJVppCore getFutureJVppCore() { + return futureJVppCore; + } + + protected abstract ClassifyAddDelTable getClassifyAddDelTableRequest(@Nonnull final PacketHandling action, + @Nonnull final T ace, + final int nextTableIndex); + + protected abstract ClassifyAddDelSession getClassifyAddDelSessionRequest(@Nonnull final PacketHandling action, + @Nonnull final T ace, + final int nextTableIndex); + + protected abstract void setClassifyTable(@Nonnull final InputAclSetInterface request, final int tableIndex); + + public final void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces, + @Nonnull final InputAclSetInterface request) + throws VppBaseCallException, WriteTimeoutException { + final PacketHandling action = aces.stream().map(ace -> ace.getActions().getPacketHandling()).distinct() + .collect(SINGLE_ITEM_COLLECTOR); + + int firstTableIndex = -1; + int nextTableIndex = -1; + for (final Ace ace : aces) { + // Create table + session per entry. We actually need one table for each nonempty subset of params, + // so we could decrease number of tables to 109 = 15 (eth) + 31 (ip4) + 63 (ip6) for general case. + // TODO: For special cases like many ACEs of similar kind, it could be significant optimization. + + final ClassifyAddDelTable ctRequest = + getClassifyAddDelTableRequest(action, (T) ace.getMatches().getAceType(), nextTableIndex); + nextTableIndex = createClassifyTable(id, ctRequest); + createClassifySession(id, + getClassifyAddDelSessionRequest(action, (T) ace.getMatches().getAceType(), nextTableIndex)); + if (firstTableIndex == -1) { + firstTableIndex = nextTableIndex; + } + } + + setClassifyTable(request, firstTableIndex); + } + + private int createClassifyTable(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelTable request) + throws VppBaseCallException, WriteTimeoutException { + final CompletionStage<ClassifyAddDelTableReply> cs = futureJVppCore.classifyAddDelTable(request); + + final ClassifyAddDelTableReply reply = TranslateUtils.getReplyForWrite(cs.toCompletableFuture(), id); + return reply.newTableIndex; + } + + private void createClassifySession(@Nonnull final InstanceIdentifier<?> id, + @Nonnull final ClassifyAddDelSession request) + throws VppBaseCallException, WriteTimeoutException { + final CompletionStage<ClassifyAddDelSessionReply> cs = futureJVppCore.classifyAddDelSession(request); + + TranslateUtils.getReplyForWrite(cs.toCompletableFuture(), id); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java new file mode 100644 index 000000000..4744de1fe --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceEthWriter.java @@ -0,0 +1,168 @@ +/* + * 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; + +import io.fd.honeycomb.translate.v3po.util.TranslateUtils; +import java.util.List; +import javax.annotation.Nonnull; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +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.ace.type.AceEth; +import org.openvpp.jvpp.core.dto.ClassifyAddDelSession; +import org.openvpp.jvpp.core.dto.ClassifyAddDelTable; +import org.openvpp.jvpp.core.dto.InputAclSetInterface; +import org.openvpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class AceEthWriter extends AbstractAceWriter<AceEth> { + + private static final Logger LOG = LoggerFactory.getLogger(AceEthWriter.class); + + public AceEthWriter(@Nonnull final FutureJVppCore futureJVppCore) { + super(futureJVppCore); + } + + @Override + public ClassifyAddDelTable getClassifyAddDelTableRequest(@Nonnull final PacketHandling action, + @Nonnull final AceEth aceEth, + @Nonnull 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 + + if (action instanceof Permit) { + request.missNextIndex = 0; // for list of permit rules, deny (0) should be default action + } else { // deny is default value + request.missNextIndex = -1; // for list of deny rules, permit (-1) should be default action + } + + request.nextTableIndex = 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 = TranslateUtils.COLON_SPLITTER.splitToList(macAddress); + int i = 0; + for (String part : parts) { + request.mask[i++] = TranslateUtils.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 = TranslateUtils.COLON_SPLITTER.splitToList(macAddress); + int i = 6; + for (String part : parts) { + request.mask[i++] = TranslateUtils.parseHexByte(part); + } + } else if (aceEth.getSourceMacAddress() != null) { + aceIsEmpty = false; + for (int i = 6; i < 12; ++i) { + request.mask[i] = (byte) 0xff; + } + } + + if (aceIsEmpty) { + throw new IllegalArgumentException( + String.format("Ace %s does not define packet field matches", aceEth.toString())); + } + + request.skipNVectors = 0; + request.matchNVectors = request.mask.length / 16; + + // TODO: 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 + request.memorySize = 8 * 1024; + + if (LOG.isDebugEnabled()) { + LOG.debug("ACE action={}, rule={} translated to table={}.", action, aceEth, + ReflectionToStringBuilder.toString(request)); + } + return request; + } + + @Override + public ClassifyAddDelSession getClassifyAddDelSessionRequest(@Nonnull final PacketHandling action, + @Nonnull final AceEth aceEth, + @Nonnull final int tableIndex) { + final ClassifyAddDelSession request = new ClassifyAddDelSession(); + request.isAdd = 1; + request.tableIndex = tableIndex; + + if (action instanceof Permit) { + request.hitNextIndex = -1; + } // deny (0) is default value + + request.match = new byte[16]; + boolean noMatch = true; + + if (aceEth.getDestinationMacAddress() != null) { + noMatch = false; + final String macAddress = aceEth.getDestinationMacAddress().getValue(); + final List<String> parts = TranslateUtils.COLON_SPLITTER.splitToList(macAddress); + int i = 0; + for (String part : parts) { + request.match[i++] = TranslateUtils.parseHexByte(part); + } + } + + if (aceEth.getSourceMacAddress() != null) { + noMatch = false; + final String macAddress = aceEth.getSourceMacAddress().getValue(); + final List<String> parts = TranslateUtils.COLON_SPLITTER.splitToList(macAddress); + int i = 6; + for (String part : parts) { + request.match[i++] = TranslateUtils.parseHexByte(part); + } + } + + if (noMatch) { + throw new IllegalArgumentException( + String.format("Ace %s does not define neither source nor destination MAC address", aceEth.toString())); + } + + if (LOG.isDebugEnabled()) { + LOG.debug("ACE action={}, rule={} translated to session={}.", action, aceEth, + ReflectionToStringBuilder.toString(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/AceWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java new file mode 100644 index 000000000..be3889fb0 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/AceWriter.java @@ -0,0 +1,30 @@ +/* + * 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; + +import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; +import java.util.List; +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 org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.VppBaseCallException; +import org.openvpp.jvpp.core.dto.InputAclSetInterface; + +interface AceWriter { + void write(@Nonnull final InstanceIdentifier<?> id, @Nonnull final List<Ace> aces, + @Nonnull final InputAclSetInterface request) throws VppBaseCallException, WriteTimeoutException; +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java new file mode 100644 index 000000000..5eb4af5e1 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAClWriter.java @@ -0,0 +1,183 @@ +/* + * 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; + +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.acl.AclWriter; +import io.fd.honeycomb.translate.v3po.util.TranslateUtils; +import io.fd.honeycomb.translate.v3po.util.WriteTimeoutException; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletionStage; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.AclBase; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.AclKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.AccessListEntries; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.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.v3po.rev150105.ietf.acl.base.attributes.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.VppBaseCallException; +import org.openvpp.jvpp.core.dto.ClassifyAddDelTable; +import org.openvpp.jvpp.core.dto.ClassifyAddDelTableReply; +import org.openvpp.jvpp.core.dto.ClassifyTableByInterface; +import org.openvpp.jvpp.core.dto.ClassifyTableByInterfaceReply; +import org.openvpp.jvpp.core.dto.InputAclSetInterface; +import org.openvpp.jvpp.core.dto.InputAclSetInterfaceReply; +import org.openvpp.jvpp.core.future.FutureJVppCore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class IetfAClWriter { + + private static final Logger LOG = LoggerFactory.getLogger(IetfAClWriter.class); + private final FutureJVppCore jvpp; + + private Map<AclType, AceWriter> aceWriters = new HashMap<>(); + + public IetfAClWriter(@Nonnull final FutureJVppCore futureJVppCore) { + this.jvpp = Preconditions.checkNotNull(futureJVppCore, "futureJVppCore should not be null"); + aceWriters.put(AclType.ETH, new AceEthWriter(futureJVppCore)); + } + + void deleteAcl(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex) + throws WriteTimeoutException, WriteFailedException.DeleteFailedException { + final ClassifyTableByInterface request = new ClassifyTableByInterface(); + request.swIfIndex = swIfIndex; + jvpp.classifyTableByInterface(request); + + try { + final CompletionStage<ClassifyTableByInterfaceReply> cs = jvpp.classifyTableByInterface(request); + final ClassifyTableByInterfaceReply reply = TranslateUtils.getReplyForWrite(cs.toCompletableFuture(), id); + + // We remove all ACL-related classify tables for given interface (we assume we are the only classify table + // manager) + removeClassifyTable(id, reply.l2TableId); + removeClassifyTable(id, reply.ip4TableId); + removeClassifyTable(id, reply.ip6TableId); + } catch (VppBaseCallException e) { + throw new WriteFailedException.DeleteFailedException(id, e); + } + } + + private void removeClassifyTable(@Nonnull final InstanceIdentifier<?> id, final int tableIndex) + throws VppBaseCallException, WriteTimeoutException { + + if (tableIndex == -1) { + return; // classify table id is absent + } + final ClassifyAddDelTable request = new ClassifyAddDelTable(); + request.tableIndex = tableIndex; + final CompletionStage<ClassifyAddDelTableReply> cs = jvpp.classifyAddDelTable(request); + TranslateUtils.getReplyForWrite(cs.toCompletableFuture(), id); + } + + void write(@Nonnull final InstanceIdentifier<?> id, final int swIfIndex, @Nonnull final List<Acl> acls, + @Nonnull final WriteContext writeContext) + throws VppBaseCallException, WriteTimeoutException { + + // filter ACE entries and group by AceType + final Map<AclType, List<Ace>> acesByType = acls.stream() + .flatMap(acl -> aclToAceStream(acl, writeContext)) + // TODO we should not tolerate nulls, but throw some meaningful exceptions instead + .filter(ace -> ace != null && ace.getMatches() != null && ace.getMatches().getAceType() != null && + ace.getActions() != null && ace.getActions().getPacketHandling() != null) + .collect(Collectors.groupingBy(AclType::fromAce)); + + final InputAclSetInterface request = new InputAclSetInterface(); + request.isAdd = 1; + request.swIfIndex = swIfIndex; + request.l2TableIndex = -1; + request.ip4TableIndex = -1; + request.ip6TableIndex = -1; + + // 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); + + final AceWriter aceWriter = aceWriters.get(aceType); + if (aceWriter == null) { + LOG.warn("AceProcessor for {} not registered. Skipping ACE.", aceType); + } else { + aceWriter.write(id, aces, request); + } + } + + final CompletionStage<InputAclSetInterfaceReply> inputAclSetInterfaceReplyCompletionStage = + jvpp.inputAclSetInterface(request); + TranslateUtils.getReplyForWrite(inputAclSetInterfaceReplyCompletionStage.toCompletableFuture(), id); + + } + + private static Stream<Ace> aclToAceStream(@Nonnull final Acl assignedAcl, + @Nonnull final WriteContext writeContext) { + final String aclName = assignedAcl.getName(); + final Class<? extends AclBase> aclType = assignedAcl.getType(); + + // ietf-acl updates are handled first, so we use writeContext.readAfter + final Optional<org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl> + aclOptional = writeContext.readAfter(AclWriter.ACL_ID.child( + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl.class, + new AclKey(aclName, aclType))); + checkArgument(aclOptional.isPresent(), "Acl lists not configured"); + final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl + acl = aclOptional.get(); + + final AccessListEntries accessListEntries = acl.getAccessListEntries(); + checkArgument(accessListEntries != null, "access list entries not configured"); + + return accessListEntries.getAce().stream(); + } + + private enum AclType { + ETH, IP4, IP6; + + @Nonnull + private static AclType fromAce(final Ace ace) { + AclType result = null; + final AceType aceType = ace.getMatches().getAceType(); + if (aceType instanceof AceEth) { + result = ETH; + } else if (aceType instanceof AceIp) { + final AceIpVersion aceIpVersion = ((AceIp) aceType).getAceIpVersion(); + if (aceIpVersion instanceof AceIpv4) { + result = IP4; + } else { + result = IP6; + } + } + if (result == null) { + throw new IllegalArgumentException(String.format("Not supported ace type %s", aceType)); + } + return result; + } + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAclCustomizer.java new file mode 100644 index 000000000..d3e14347d --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/IetfAclCustomizer.java @@ -0,0 +1,93 @@ +/* + * 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; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.v3po.util.NamingContext; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.interfaces._interface.IetfAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.VppBaseCallException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ACLs for given interface (as defined in ietf-acl model). + * + * The customizer assumes it owns classify table management for interfaces where ietf-acl container is present. Using + * low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported and + * can result in unpredictable behaviour. + */ +public class IetfAclCustomizer implements WriterCustomizer<IetfAcl> { + + private static final Logger LOG = LoggerFactory.getLogger(IetfAclCustomizer.class); + private final IetfAClWriter aclWriter; + private final NamingContext interfaceContext; + + 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"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, @Nonnull final IetfAcl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Adding ACLs for interface={}(id={}): {}", ifName, ifIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + try { + aclWriter.write(id, ifIndex, accessLists.getAcl(), writeContext); + } catch (VppBaseCallException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, + @Nonnull final IetfAcl dataBefore, @Nonnull final IetfAcl dataAfter, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + LOG.debug("ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, + @Nonnull final IetfAcl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String ifName = id.firstKeyOf(Interface.class).getName(); + final int ifIndex = interfaceContext.getIndex(ifName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for interface={}(id={}): {}", ifName, ifIndex, dataBefore); + aclWriter.deleteAcl(id, ifIndex); + } +} diff --git a/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java new file mode 100644 index 000000000..894269101 --- /dev/null +++ b/v3po/v3po2vpp/src/main/java/io/fd/honeycomb/translate/v3po/interfaces/acl/SubInterfaceIetfAclCustomizer.java @@ -0,0 +1,103 @@ +/* + * 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; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.v3po.util.NamingContext; +import io.fd.honeycomb.translate.v3po.util.SubInterfaceUtils; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.InterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.v3po.rev150105.ietf.acl.base.attributes.AccessLists; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.interfaces._interface.sub.interfaces.SubInterfaceKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.vlan.rev150527.sub._interface.base.attributes.IetfAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.openvpp.jvpp.VppBaseCallException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Customizer for enabling/disabling ACLs for given sub-interface (as defined in ietf-acl model). + * + * The customizer assumes it owns classify table management for sub-interfaces where ietf-acl container is present. + * Using low level classifier model or direct changes to classify tables in combination with ietf-acls are not supported + * and can result in unpredictable behaviour. + */ +public class SubInterfaceIetfAclCustomizer implements WriterCustomizer<IetfAcl> { + + private static final Logger LOG = LoggerFactory.getLogger(SubInterfaceIetfAclCustomizer.class); + private final IetfAClWriter aclWriter; + private final NamingContext interfaceContext; + + 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"); + } + + private String getSubInterfaceName(@Nonnull final InstanceIdentifier<IetfAcl> id) { + final InterfaceKey parentInterfacekey = id.firstKeyOf(Interface.class); + final SubInterfaceKey subInterfacekey = id.firstKeyOf(SubInterface.class); + return SubInterfaceUtils + .getSubInterfaceName(parentInterfacekey.getName(), subInterfacekey.getIdentifier().intValue()); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, @Nonnull final IetfAcl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Adding IETF-ACL for sub-interface: {}(id={}): {}", subInterfaceName, subInterfaceIndex, dataAfter); + + final AccessLists accessLists = dataAfter.getAccessLists(); + checkArgument(accessLists != null && accessLists.getAcl() != null, + "ietf-acl container does not define acl list"); + + try { + aclWriter.write(id, subInterfaceIndex, accessLists.getAcl(), writeContext); + } catch (VppBaseCallException e) { + throw new WriteFailedException.CreateFailedException(id, dataAfter, e); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, + @Nonnull final IetfAcl dataBefore, @Nonnull final IetfAcl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + LOG.debug("Sub-interface ACLs update: removing previously configured ACLs"); + deleteCurrentAttributes(id, dataBefore, writeContext); + LOG.debug("Sub-interface ACLs update: adding updated ACLs"); + writeCurrentAttributes(id, dataAfter, writeContext); + LOG.debug("Sub-interface ACLs update was successful"); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<IetfAcl> id, + @Nonnull final IetfAcl dataBefore, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final String subInterfaceName = getSubInterfaceName(id); + final int subInterfaceIndex = interfaceContext.getIndex(subInterfaceName, writeContext.getMappingContext()); + LOG.debug("Removing ACLs for sub-interface={}(id={}): {}", subInterfaceName, subInterfaceIndex, dataBefore); + aclWriter.deleteAcl(id, subInterfaceIndex); + } +} |