diff options
Diffstat (limited to 'acl/acl-impl/src')
38 files changed, 3444 insertions, 0 deletions
diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java new file mode 100644 index 000000000..f15119be6 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/AclModule.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl; + +import com.google.common.annotations.VisibleForTesting; +import com.google.inject.AbstractModule; +import com.google.inject.Provider; +import com.google.inject.Singleton; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Names; +import io.fd.hc2vpp.acl.read.factory.InterfaceAclReaderFactory; +import io.fd.hc2vpp.acl.write.factory.InterfaceAclWriterFactory; +import io.fd.hc2vpp.acl.write.factory.VppAclWriterFactory; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +import net.jmob.guice.conf.core.ConfigurationModule; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AclModule extends AbstractModule { + + public static final String STANDARD_ACL_CONTEXT_NAME = "standard-acl-context"; + public static final String STANDARD_LEARNED_ACL_NAME_PREFIX = "standard-learned-acl-"; + public static final String MAC_IP_ACL_CONTEXT_NAME = "mac-ip-acl-context"; + public static final String MAC_IP_LEARNED_ACL_NAME_PREFIX = "mac-ip-acl-context"; + + private static final Logger LOG = LoggerFactory.getLogger(AclModule.class); + + private final Class<? extends Provider<FutureJVppAclFacade>> jvppAclProviderClass; + + public AclModule() { + this(JVppAclProvider.class); + } + + @VisibleForTesting + AclModule(@Nonnull final Class<? extends Provider<FutureJVppAclFacade>> jvppAclProviderClass) { + this.jvppAclProviderClass = jvppAclProviderClass; + } + + @Override + protected void configure() { + LOG.info("Configuring module Acl"); + install(ConfigurationModule.create()); + + // binds JVpp Acl future facade + bind(FutureJVppAclFacade.class).toProvider(jvppAclProviderClass).in(Singleton.class); + + bind(NamingContext.class).annotatedWith(Names.named(STANDARD_ACL_CONTEXT_NAME)) + .toInstance(new NamingContext(STANDARD_LEARNED_ACL_NAME_PREFIX, STANDARD_ACL_CONTEXT_NAME)); + + bind(NamingContext.class).annotatedWith(Names.named(MAC_IP_ACL_CONTEXT_NAME)) + .toInstance(new NamingContext(MAC_IP_LEARNED_ACL_NAME_PREFIX, MAC_IP_ACL_CONTEXT_NAME)); + + final Multibinder<WriterFactory> writerFactoryMultibinder = + Multibinder.newSetBinder(binder(), WriterFactory.class); + writerFactoryMultibinder.addBinding().to(VppAclWriterFactory.class); + writerFactoryMultibinder.addBinding().to(InterfaceAclWriterFactory.class); + + final Multibinder<ReaderFactory> readerFactoryMultibinder = + Multibinder.newSetBinder(binder(), ReaderFactory.class); + readerFactoryMultibinder.addBinding().to(InterfaceAclReaderFactory.class); + + LOG.info("Module Acl successfully configured"); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/JVppAclProvider.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/JVppAclProvider.java new file mode 100644 index 000000000..b3caa0e9c --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/JVppAclProvider.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl; + +import com.google.inject.Inject; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.honeycomb.infra.distro.ProviderTrait; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.VppBaseCallException; +import io.fd.vpp.jvpp.acl.JVppAclImpl; +import io.fd.vpp.jvpp.acl.dto.AclPluginGetVersion; +import io.fd.vpp.jvpp.acl.dto.AclPluginGetVersionReply; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.io.IOException; +import java.util.concurrent.TimeoutException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class JVppAclProvider extends ProviderTrait<FutureJVppAclFacade> implements JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(JVppAclProvider.class); + + @Inject + private JVppRegistry registry; + + private static JVppAclImpl initAclApi() { + final JVppAclImpl jvppAcl = new JVppAclImpl(); + // Free jvpp-acl plugin's resources on shutdown + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + LOG.info("Unloading jvpp-acl plugin"); + jvppAcl.close(); + LOG.info("Successfully unloaded jvpp-acl plugin"); + } + }); + return jvppAcl; + } + + @Override + protected FutureJVppAclFacade create() { + try { + return reportVersionAndGet(initAclApi()); + } catch (IOException e) { + throw new IllegalStateException("Unable to open VPP management connection", e); + } catch (TimeoutException | VppBaseCallException e) { + throw new IllegalStateException("Unable to load ACL plugin version", e); + } + } + + private FutureJVppAclFacade reportVersionAndGet(final JVppAclImpl jvppAcl) + throws IOException, TimeoutException, VppBaseCallException { + final FutureJVppAclFacade futureFacade = new FutureJVppAclFacade(registry, jvppAcl); + final AclPluginGetVersionReply pluginVersion = + getReply(futureFacade.aclPluginGetVersion(new AclPluginGetVersion()).toCompletableFuture()); + LOG.info("Acl plugin successfully loaded[version {}.{}]", pluginVersion.major, pluginVersion.minor); + return futureFacade; + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppAclCustomizer.java new file mode 100644 index 000000000..90f2b2fe2 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppAclCustomizer.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.read; + + +import com.google.common.base.Optional; +import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ListReaderCustomizer; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager.DumpCacheManagerBuilder; +import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor; +import io.fd.honeycomb.translate.util.read.cache.EntityDumpPostProcessingFunction; +import io.fd.vpp.jvpp.acl.dto.AclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceListDump; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.EgressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.IngressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAcls; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAclsBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAclsKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppAcl; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class VppAclCustomizer extends FutureJVppAclCustomizer + implements ListReaderCustomizer<VppAcls, VppAclsKey, VppAclsBuilder>, JvppReplyConsumer, ByteDataTranslator { + + private final NamingContext interfaceContext; + private final NamingContext standardAclContext; + /** + * true == ingress + * false == egress + */ + private final boolean input; + private final DumpCacheManager<AclInterfaceListDetailsReplyDump, Integer> aclReferenceDumpManager; + private final DumpCacheManager<AclDetailsReplyDump, Integer> aclDumpManager; + + public VppAclCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext standardAclContext, + final boolean input) { + super(jVppAclFacade); + this.interfaceContext = interfaceContext; + this.standardAclContext = standardAclContext; + this.input = input; + + aclReferenceDumpManager = + new DumpCacheManagerBuilder<AclInterfaceListDetailsReplyDump, Integer>() + .withExecutor(createAclReferenceDumpExecutor()) + .withPostProcessingFunction(input + ? createInputAclFilter() + : createOutputAclFilter()) + .acceptOnly(AclInterfaceListDetailsReplyDump.class) + .build(); + + aclDumpManager = new DumpCacheManagerBuilder<AclDetailsReplyDump, Integer>() + .withExecutor(createAclExecutor()) + .acceptOnly(AclDetailsReplyDump.class) + .build(); + } + + private EntityDumpExecutor<AclDetailsReplyDump, Integer> createAclExecutor() { + return (identifier, params) -> { + AclDump request = new AclDump(); + request.aclIndex = params; + return getReplyForRead(getjVppAclFacade().aclDump(request).toCompletableFuture(), identifier); + }; + } + + private EntityDumpPostProcessingFunction<AclInterfaceListDetailsReplyDump> createInputAclFilter() { + return dump -> { + // filters acl's to first N(those are input ones) + dump.aclInterfaceListDetails = dump.aclInterfaceListDetails + .stream() + .map(iface -> { + iface.acls = Arrays.copyOfRange(iface.acls, 0, iface.nInput - 1); + return iface; + }) + .collect(Collectors.toList()); + return dump; + }; + } + + private EntityDumpPostProcessingFunction<AclInterfaceListDetailsReplyDump> createOutputAclFilter() { + return dump -> { + // filters acl's to last N(those are output ones) + dump.aclInterfaceListDetails = dump.aclInterfaceListDetails + .stream() + .map(iface -> { + iface.acls = Arrays.copyOfRange(iface.acls, iface.nInput, iface.acls.length); + return iface; + }) + .collect(Collectors.toList()); + return dump; + }; + } + + private EntityDumpExecutor<AclInterfaceListDetailsReplyDump, Integer> createAclReferenceDumpExecutor() { + return (identifier, params) -> { + AclInterfaceListDump dumpRequest = new AclInterfaceListDump(); + dumpRequest.swIfIndex = params; + return getReplyForRead(getjVppAclFacade().aclInterfaceListDump(dumpRequest).toCompletableFuture(), + identifier); + }; + } + + @Nonnull + @Override + public List<VppAclsKey> getAllIds(@Nonnull final InstanceIdentifier<VppAcls> id, @Nonnull final ReadContext context) + throws ReadFailedException { + + final String parentInterfaceName = id.firstKeyOf(Interface.class).getName(); + final int parentInterfaceIndex = interfaceContext.getIndex(parentInterfaceName, context.getMappingContext()); + + final Optional<AclInterfaceListDetailsReplyDump> dumpReply = + aclReferenceDumpManager.getDump(id, context.getModificationCache(), parentInterfaceIndex); + + if (dumpReply.isPresent() && !dumpReply.get().aclInterfaceListDetails.isEmpty()) { + return Arrays.stream(dumpReply.get().aclInterfaceListDetails.get(0).acls) + .mapToObj(aclIndex -> standardAclContext.getName(aclIndex, context.getMappingContext())) + .map(aclName -> new VppAclsKey(aclName, VppAcl.class)) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> builder, @Nonnull final List<VppAcls> readData) { + if (input) { + IngressBuilder.class.cast(builder).setVppAcls(readData); + } else { + EgressBuilder.class.cast(builder).setVppAcls(readData); + } + } + + @Nonnull + @Override + public VppAclsBuilder getBuilder(@Nonnull final InstanceIdentifier<VppAcls> id) { + return new VppAclsBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VppAcls> id, + @Nonnull final VppAclsBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final VppAclsKey vppAclsKey = id.firstKeyOf(VppAcls.class); + final String aclName = vppAclsKey.getName(); + final int aclIndex = standardAclContext.getIndex(aclName, ctx.getMappingContext()); + + final Optional<AclDetailsReplyDump> dumpReply = + aclDumpManager.getDump(id, ctx.getModificationCache(), aclIndex); + + if (dumpReply.isPresent() && !dumpReply.get().aclDetails.isEmpty()) { + // FIXME (model expects hex string, but tag is written and read as ascii string) + // decide how tag should be handled (model change might be needed). + builder.setName(aclName); + builder.setType(vppAclsKey.getType()); + builder.setTag(new HexString(printHexBinary(dumpReply.get().aclDetails.get(0).tag))); + } else { + throw new ReadFailedException(id, + new IllegalArgumentException(String.format("Acl with name %s not found", aclName))); + } + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppMacIpAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppMacIpAclCustomizer.java new file mode 100644 index 000000000..982d89b9a --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/VppMacIpAclCustomizer.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.read; + +import static io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor.NO_PARAMS; + +import com.google.common.base.Optional; +import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.ModificationCache; +import io.fd.honeycomb.translate.read.ReadContext; +import io.fd.honeycomb.translate.read.ReadFailedException; +import io.fd.honeycomb.translate.spi.read.ReaderCustomizer; +import io.fd.honeycomb.translate.util.read.cache.DumpCacheManager; +import io.fd.honeycomb.translate.util.read.cache.EntityDumpExecutor; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetails; +import io.fd.vpp.jvpp.acl.dto.MacipAclDetailsReplyDump; +import io.fd.vpp.jvpp.acl.dto.MacipAclDump; +import io.fd.vpp.jvpp.acl.dto.MacipAclInterfaceGet; +import io.fd.vpp.jvpp.acl.dto.MacipAclInterfaceGetReply; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.HexString; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.IngressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAclBuilder; +import org.opendaylight.yangtools.concepts.Builder; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class VppMacIpAclCustomizer extends FutureJVppAclCustomizer + implements ReaderCustomizer<VppMacipAcl, VppMacipAclBuilder>, JvppReplyConsumer, ByteDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(VppMacIpAclCustomizer.class); + + private final DumpCacheManager<MacipAclDetailsReplyDump, Integer> macIpAclDumpManager; + private final DumpCacheManager<MacipAclInterfaceGetReply, Void> interfaceMacIpAclDumpManager; + private final NamingContext interfaceContext; + private final NamingContext macIpAclContext; + + public VppMacIpAclCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext macIpAclContext) { + super(jVppAclFacade); + + // for dumping of Mac-ip details + macIpAclDumpManager = new DumpCacheManager.DumpCacheManagerBuilder<MacipAclDetailsReplyDump, Integer>() + .withExecutor(createMacIpDumpExecutor()) + .acceptOnly(MacipAclDetailsReplyDump.class) + .build(); + + // for dumping of reference on interface + interfaceMacIpAclDumpManager = new DumpCacheManager.DumpCacheManagerBuilder<MacipAclInterfaceGetReply, Void>() + .withExecutor(createInterfaceMacIpDumpExecutor()) + .acceptOnly(MacipAclInterfaceGetReply.class) + .build(); + this.interfaceContext = interfaceContext; + this.macIpAclContext = macIpAclContext; + } + + private EntityDumpExecutor<MacipAclDetailsReplyDump, Integer> createMacIpDumpExecutor() { + return (identifier, params) -> { + MacipAclDump request = new MacipAclDump(); + request.aclIndex = params; + + return getReplyForRead(getjVppAclFacade().macipAclDump(request).toCompletableFuture(), identifier); + }; + } + + private EntityDumpExecutor<MacipAclInterfaceGetReply, Void> createInterfaceMacIpDumpExecutor() { + return (identifier, params) -> getReplyForRead( + getjVppAclFacade().macipAclInterfaceGet(new MacipAclInterfaceGet()).toCompletableFuture(), + identifier); + } + + @Nonnull + @Override + public VppMacipAclBuilder getBuilder(@Nonnull final InstanceIdentifier<VppMacipAcl> id) { + return new VppMacipAclBuilder(); + } + + @Override + public void readCurrentAttributes(@Nonnull final InstanceIdentifier<VppMacipAcl> id, + @Nonnull final VppMacipAclBuilder builder, + @Nonnull final ReadContext ctx) throws ReadFailedException { + final String interfaceName = id.firstKeyOf(Interface.class).getName(); + final MappingContext mappingContext = ctx.getMappingContext(); + final int interfaceIndex = interfaceContext.getIndex(interfaceName, mappingContext); + final ModificationCache modificationCache = ctx.getModificationCache(); + final Optional<MacipAclInterfaceGetReply> interfacesMacIpDumpReply = + interfaceMacIpAclDumpManager.getDump(id, modificationCache, NO_PARAMS); + + if (interfacesMacIpDumpReply.isPresent() && interfaceIndex < interfacesMacIpDumpReply.get().count) { + final int aclIndex = interfacesMacIpDumpReply.get().acls[interfaceIndex]; + + final Optional<MacipAclDetailsReplyDump> macIpDumpReply = + macIpAclDumpManager.getDump(id, modificationCache, aclIndex); + + if (macIpDumpReply.isPresent() && !macIpDumpReply.get().macipAclDetails.isEmpty()) { + final MacipAclDetails details = macIpDumpReply.get().macipAclDetails.get(0); + + builder.setName(macIpAclContext.getName(aclIndex, mappingContext)) + .setType( + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppMacipAcl.class) + .setTag(new HexString(printHexBinary(details.tag))); + } else { + // this is invalid state(Interface in VPP will act as "deny-all" for security reasons), but generally + // it should not happen + throw new ReadFailedException(id, + new IllegalStateException(String.format("ACC with index %s not found in VPP", aclIndex))); + } + } else { + // this is valid state, so just logging + LOG.info("No Mac-ip ACL specified for Interface name={},index={}", interfaceName, interfaceIndex); + } + } + + @Override + public void merge(@Nonnull final Builder<? extends DataObject> parentBuilder, + @Nonnull final VppMacipAcl readValue) { + IngressBuilder.class.cast(parentBuilder).setVppMacipAcl(readValue); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/factory/InterfaceAclReaderFactory.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/factory/InterfaceAclReaderFactory.java new file mode 100644 index 000000000..89520620d --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/read/factory/InterfaceAclReaderFactory.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.read.factory; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.acl.AclModule; +import io.fd.hc2vpp.acl.read.VppAclCustomizer; +import io.fd.hc2vpp.acl.read.VppMacIpAclCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.impl.read.GenericListReader; +import io.fd.honeycomb.translate.impl.read.GenericReader; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.read.registry.ModifiableReaderRegistryBuilder; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceStateAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclInterfaceStateAugmentationBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.AclBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Egress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.EgressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.IngressBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAcls; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class InterfaceAclReaderFactory implements ReaderFactory { + + @Inject + private FutureJVppAclFacade futureAclFacade; + + @Inject + @Named(AclModule.STANDARD_ACL_CONTEXT_NAME) + private NamingContext standardAclContext; + + @Inject + @Named(AclModule.MAC_IP_ACL_CONTEXT_NAME) + private NamingContext macIpAClContext; + + @Inject + @Named("interface-context") + private NamingContext interfaceContext; + + private static final InstanceIdentifier<Interface> + IFC_ID = InstanceIdentifier.create(InterfacesState.class).child(Interface.class); + private static final InstanceIdentifier<VppAclInterfaceStateAugmentation> VPP_ACL_AUG_IID = + IFC_ID.augmentation(VppAclInterfaceStateAugmentation.class); + private static final InstanceIdentifier<Acl> ACL_IID = VPP_ACL_AUG_IID.child(Acl.class); + + @Override + public void init(@Nonnull final ModifiableReaderRegistryBuilder registry) { + registry.addStructuralReader(VPP_ACL_AUG_IID, VppAclInterfaceStateAugmentationBuilder.class); + registry.addStructuralReader(ACL_IID, AclBuilder.class); + + final InstanceIdentifier<Ingress> ingressInstanceIdentifier = ACL_IID.child(Ingress.class); + registry.addStructuralReader(ingressInstanceIdentifier, IngressBuilder.class); + registry.addAfter(new GenericListReader<>(ingressInstanceIdentifier.child(VppAcls.class), + new VppAclCustomizer(futureAclFacade, interfaceContext, standardAclContext, true)), IFC_ID); + registry.addAfter(new GenericReader<>(ingressInstanceIdentifier.child(VppMacipAcl.class), + new VppMacIpAclCustomizer(futureAclFacade, interfaceContext, macIpAClContext)), IFC_ID); + + final InstanceIdentifier<Egress> egressInstanceIdentifier = ACL_IID.child(Egress.class); + registry.addStructuralReader(egressInstanceIdentifier, EgressBuilder.class); + registry.addAfter(new GenericListReader<>(egressInstanceIdentifier.child(VppAcls.class), + new VppAclCustomizer(futureAclFacade, interfaceContext, standardAclContext, false)), IFC_ID); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/FutureJVppAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/FutureJVppAclCustomizer.java new file mode 100644 index 000000000..e2d6bee58 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/FutureJVppAclCustomizer.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; + +/** + * Holds reference to jvpp acl implementation + */ +public abstract class FutureJVppAclCustomizer { + + private final FutureJVppAclFacade jVppAclFacade; + + public FutureJVppAclCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade) { + this.jVppAclFacade = checkNotNull(jVppAclFacade, "JVpp Acl Future api is null"); + } + + public FutureJVppAclFacade getjVppAclFacade() { + return jVppAclFacade; + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/AceConverter.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/AceConverter.java new file mode 100644 index 000000000..325787821 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/AceConverter.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.ace; + +import io.fd.hc2vpp.acl.util.ace.extractor.MacIpAceDataExtractor; +import io.fd.hc2vpp.acl.util.ace.extractor.StandardAceDataExtractor; +import io.fd.hc2vpp.acl.util.protocol.ProtoPreBindRuleProducer; +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; +import java.util.List; +import java.util.stream.Collectors; +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.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.VppAce; +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.VppMacipAce; + +/** + * Convert Ace's to vpp rules + */ +public interface AceConverter extends MacIpAceDataExtractor, StandardAceDataExtractor, ProtoPreBindRuleProducer { + + + default MacipAclRule[] convertToMacIpAclRules(@Nonnull final List<Ace> aces) { + return aces.stream() + .map(ace -> { + final VppMacipAce macIpAce = fromMacIpAce(ace); + + MacipAclRule rule = new MacipAclRule(); + + rule.srcMac = sourceMacAsBytes(macIpAce); + rule.srcMacMask = sourceMacMaskAsBytes(macIpAce); + rule.isPermit = macIpAction(ace); + + if (macIpIsIpv6(macIpAce)) { + rule.isIpv6 = 1; + rule.srcIpAddr = ipv6Address(macIpAce); + rule.srcIpPrefixLen = ipv6AddressPrefix(macIpAce); + } else { + rule.isIpv6 = 0; + rule.srcIpAddr = ipv4Address(macIpAce); + rule.srcIpPrefixLen = ipv4AddressPrefix(macIpAce); + } + + return rule; + }) + .collect(Collectors.toList()) + .toArray(new MacipAclRule[aces.size()]); + } + + default AclRule[] convertToStandardAclRules(@Nonnull final List<Ace> aces) { + return aces.stream() + .map(ace -> { + final VppAce standardAce = fromStandardAce(ace); + + // pre-bind rule with protocol based attributes + AclRule rule = createPreBindRule(standardAce); + + rule.isPermit = standardAction(ace); + + if (standardIsIpv6(ace)) { + rule.isIpv6 = 1; + rule.srcIpAddr = ipv6SourceAddress(standardAce); + rule.srcIpPrefixLen = ipv6SourceAddressPrefix(standardAce); + rule.dstIpAddr = ipv6DestinationAddress(standardAce); + rule.dstIpPrefixLen = ipv6DestinationAddressPrefix(standardAce); + } else { + rule.isIpv6 = 0; + rule.srcIpAddr = ipv4SourceAddress(standardAce); + rule.srcIpPrefixLen = ipv4SourceAddressPrefix(standardAce); + rule.dstIpAddr = ipv4DestinationAddress(standardAce); + rule.dstIpPrefixLen = ipv4DestinationAddressPrefix(standardAce); + } + + + return rule; + }) + .collect(Collectors.toList()) + .toArray(new AclRule[aces.size()]); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/MacIpAceDataExtractor.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/MacIpAceDataExtractor.java new file mode 100644 index 000000000..a0d19902a --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/MacIpAceDataExtractor.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.ace.extractor; + +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +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.actions.packet.handling.Deny; +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.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppMacipAceIpv4HeaderFields; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppMacipAceIpv6HeaderFields; +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.VppMacipAce; +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.vpp.ace.vpp.ace.nodes.ace.ip.version.AceIpv6; + +public interface MacIpAceDataExtractor extends AddressTranslator { + + default VppMacipAce fromMacIpAce(@Nonnull final Ace ace) { + return VppMacipAce.class.cast(ace.getMatches().getAceType()); + } + + default boolean macIpIsIpv6(@Nonnull final VppMacipAce ace) { + return ace.getVppMacipAceNodes().getAceIpVersion() instanceof AceIpv6; + } + + default byte[] sourceMacAsBytes(@Nonnull final VppMacipAce ace) { + return macToByteArray(ace.getVppMacipAceNodes().getSourceMacAddress().getValue()); + } + + default byte[] sourceMacMaskAsBytes(@Nonnull final VppMacipAce ace) { + return macToByteArray(ace.getVppMacipAceNodes().getSourceMacAddressMask().getValue()); + } + + default byte[] ipv4Address(@Nonnull final VppMacipAce ace) { + return ipv4AddressPrefixToArray( + VppMacipAceIpv4HeaderFields.class.cast(ace.getVppMacipAceNodes().getAceIpVersion()).getSourceIpv4Network()); + } + + default byte ipv4AddressPrefix(@Nonnull final VppMacipAce ace) { + return extractPrefix( + VppMacipAceIpv4HeaderFields.class.cast(ace.getVppMacipAceNodes().getAceIpVersion()).getSourceIpv4Network()); + } + + default byte[] ipv6Address(@Nonnull final VppMacipAce ace) { + return ipv6AddressPrefixToArray( + VppMacipAceIpv6HeaderFields.class.cast(ace.getVppMacipAceNodes().getAceIpVersion()).getSourceIpv6Network()); + } + + default byte ipv6AddressPrefix(@Nonnull final VppMacipAce ace) { + return extractPrefix( + VppMacipAceIpv6HeaderFields.class.cast(ace.getVppMacipAceNodes().getAceIpVersion()).getSourceIpv6Network()); + } + + /** + * Only 0 and 1 are allowed for mac-ip + */ + default byte macIpAction(@Nonnull final Ace ace) { + final PacketHandling action = ace.getActions().getPacketHandling(); + if (action instanceof Permit) { + return 1; + } else if (action instanceof Deny) { + return 0; + } else { + throw new IllegalArgumentException( + String.format("Unsupported packet-handling action %s for ACE %s", action, ace)); + } + } + +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/StandardAceDataExtractor.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/StandardAceDataExtractor.java new file mode 100644 index 000000000..2e7ccbdc7 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/ace/extractor/StandardAceDataExtractor.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.ace.extractor; + + +import com.google.common.collect.ImmutableMap; +import io.fd.hc2vpp.acl.util.protocol.ProtoPreBindRuleProducer; +import io.fd.hc2vpp.common.translate.util.AddressTranslator; +import java.util.Map; +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.actions.packet.handling.Deny; +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.packet.fields.rev160708.AclIpv4HeaderFields; +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.access.lists.acl.access.list.entries.ace.actions.packet.handling.Stateful; +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.VppAce; +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.vpp.ace.vpp.ace.nodes.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.vpp.ace.vpp.ace.nodes.ace.ip.version.AceIpv6; + + +public interface StandardAceDataExtractor extends AddressTranslator, ProtoPreBindRuleProducer { + + /** + * Allowed packet-processing actions for Acl's + */ + Map<Class<? extends PacketHandling>, Integer> ACTION_VALUE_PAIRS = ImmutableMap.of(Deny.class, 0, Permit.class, 1, + Stateful.class, 2); + + default VppAce fromStandardAce(@Nonnull final Ace ace) { + return VppAce.class.cast(ace.getMatches().getAceType()); + } + + default boolean standardIsIpv6(@Nonnull final Ace ace) { + return VppAce.class.cast(ace.getMatches().getAceType()).getVppAceNodes().getAceIpVersion() instanceof AceIpv6; + } + + default byte[] ipv4SourceAddress(@Nonnull final VppAce ace) { + return ipv4AddressPrefixToArray( + AclIpv4HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getSourceIpv4Network()); + } + + default byte ipv4SourceAddressPrefix(@Nonnull final VppAce ace) { + return extractPrefix(AclIpv4HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getSourceIpv4Network()); + } + + default byte[] ipv4DestinationAddress(@Nonnull final VppAce ace) { + return ipv4AddressPrefixToArray( + AclIpv4HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getDestinationIpv4Network()); + } + + default byte ipv4DestinationAddressPrefix(@Nonnull final VppAce ace) { + return extractPrefix(AceIpv4.class.cast(ace.getVppAceNodes().getAceIpVersion()).getDestinationIpv4Network()); + } + + default byte[] ipv6SourceAddress(@Nonnull final VppAce ace) { + return ipv6AddressPrefixToArray( + AclIpv6HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getSourceIpv6Network()); + } + + default byte ipv6SourceAddressPrefix(@Nonnull final VppAce ace) { + return extractPrefix(AclIpv6HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getSourceIpv6Network()); + } + + default byte[] ipv6DestinationAddress(@Nonnull final VppAce ace) { + return ipv6AddressPrefixToArray( + AclIpv6HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getDestinationIpv6Network()); + } + + default byte ipv6DestinationAddressPrefix(@Nonnull final VppAce ace) { + return extractPrefix(AclIpv6HeaderFields.class.cast(ace.getVppAceNodes().getAceIpVersion()).getDestinationIpv6Network()); + } + + default byte standardAction(@Nonnull final Ace ace) { + final PacketHandling action = ace.getActions().getPacketHandling(); + return ACTION_VALUE_PAIRS.get(ACTION_VALUE_PAIRS.keySet().stream() + .filter(aClass -> aClass.isInstance(action)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException( + String.format("Unsupported packet-handling action %s for ACE %s", action, + ace.getRuleName())))).byteValue(); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclDataExtractor.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclDataExtractor.java new file mode 100644 index 000000000..77e58fe0c --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclDataExtractor.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.acl; + +import java.nio.charset.StandardCharsets; +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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.Ace; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Matches; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.VppAce; +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.VppMacipAce; + +/** + * Extracts data from Acls. + * Expects data validated by {@link AclValidator} + */ +public interface AclDataExtractor { + + /** + * Checks if provided {@link Acl} has aces of type {@link VppAce} + */ + default boolean isStandardAcl(@Nonnull final Acl acl) { + return acl.getAccessListEntries().getAce().stream() + .map(Ace::getMatches) + .map(Matches::getAceType) + .filter(aceType -> aceType instanceof VppAce) + .findAny() + .isPresent(); + } + + /** + * Checks if provided {@link Acl} has aces of type {@link VppMacipAce} + */ + default boolean isMacIpAcl(@Nonnull final Acl acl) { + return acl.getAccessListEntries().getAce().stream() + .map(Ace::getMatches) + .map(Matches::getAceType) + .filter(aceType -> aceType instanceof VppMacipAce) + .findAny() + .isPresent(); + } + + default List<Ace> getAces(@Nonnull final Acl acl) { + return acl.getAccessListEntries().getAce(); + } + + /** + * Convert {@link Acl} name to byte array as UTF_8 + */ + default byte[] getAclNameAsBytes(@Nonnull final Acl acl) { + return acl.getAclName().getBytes(StandardCharsets.UTF_8); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java new file mode 100644 index 000000000..3779b82bf --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclValidator.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.acl; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +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.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; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.Matches; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.acl.access.list.entries.ace.matches.AceType; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppAcl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppMacipAcl; +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.VppAce; +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.VppMacipAce; + +/** + * Validate Acl data if processable by vpp + */ +public interface AclValidator { + + Set<Class<? extends AclBase>> SUPPORTED_ACL_TYPES = ImmutableSet.of(VppAcl.class, VppMacipAcl.class); + + Map<Class<? extends AclBase>, Class<? extends AceType>> ACL_ACE_PAIRS = ImmutableMap.of( + VppAcl.class, VppAce.class, + VppMacipAcl.class, VppMacipAce.class); + + static void isSupportedAclType(final Acl acl) { + checkArgument(SUPPORTED_ACL_TYPES.contains(acl.getAclType()), + "Unsupported Acl type %s detected for acl %s, allowed types are %s", acl.getAclType(), + acl.getAclName(), SUPPORTED_ACL_TYPES); + } + + static void hasConsistentAceTypeForAclType(final Acl acl) { + checkTypesSame(acl.getAccessListEntries().getAce(), acl.getAclName(), + checkNotNull(ACL_ACE_PAIRS.get(acl.getAclType()), "Unsupported ACL type %s for ACL %s", + acl.getAclType(), acl.getAclName())); + } + + static void checkTypesSame(final List<Ace> aces, final String aclName, final Class<? extends AceType> aceType) { + final Set<AceType> unsupportedAceTypes = aces.stream() + .map(Ace::getMatches) + .map(Matches::getAceType) + .filter(aceType::equals) + .collect(Collectors.toSet()); + checkArgument(unsupportedAceTypes.isEmpty(), "Detected unsupported ace types [%s] for ACL %s, expected %s", + unsupportedAceTypes, aclName, aceType); + } + + static void hasAceList(final Acl acl) { + //checks if aces are defined + checkArgument(!checkNotNull(checkNotNull(acl.getAccessListEntries(), "No access list entries defined") + .getAce(), "No aces defined") + .isEmpty(), "Empty ace list defined"); + } + + default void validateAcl(@Nonnull final Acl acl) { + hasAceList(acl); + isSupportedAclType(acl); + hasConsistentAceTypeForAclType(acl); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclWriter.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclWriter.java new file mode 100644 index 000000000..1fecc6c5b --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/acl/AclWriter.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.acl; + +import io.fd.hc2vpp.acl.util.ace.AceConverter; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.AclAddReplace; +import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply; +import io.fd.vpp.jvpp.acl.dto.AclDel; +import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclDel; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.access.lists.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Write standard and mac-ip acls + */ +public interface AclWriter extends AclDataExtractor, AceConverter, JvppReplyConsumer { + + int ACL_INDEX_CREATE_NEW = -1; + + default void addStandardAcl(@Nonnull final FutureJVppAclFacade futureFacade, + @Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl acl, + @Nonnull final NamingContext standardAclContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + + final AclAddReplace request = new AclAddReplace(); + + request.tag = getAclNameAsBytes(acl); + request.aclIndex = ACL_INDEX_CREATE_NEW; + request.r = convertToStandardAclRules(getAces(acl)); + request.count = request.r.length; + + final AclAddReplaceReply reply = + getReplyForWrite(futureFacade.aclAddReplace(request).toCompletableFuture(), id); + + // maps new acl to returned index + standardAclContext.addName(reply.aclIndex, acl.getAclName(), mappingContext); + } + + // according to vpp team, this was tested extensively, and should work + default void updateStandardAcl(@Nonnull final FutureJVppAclFacade futureFacade, + @Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl acl, + @Nonnull final NamingContext standardAclContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + + final AclAddReplace request = new AclAddReplace(); + + request.tag = getAclNameAsBytes(acl); + // by setting existing index, request is resolved as update + request.aclIndex = standardAclContext.getIndex(acl.getAclName(), mappingContext); + request.r = convertToStandardAclRules(getAces(acl)); + request.count = request.r.length; + + getReplyForWrite(futureFacade.aclAddReplace(request).toCompletableFuture(), id); + + } + + + default void deleteStandardAcl(@Nonnull final FutureJVppAclFacade futureFacade, + @Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl acl, + @Nonnull final NamingContext standardAclContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + + final AclDel request = new AclDel(); + final String aclName = acl.getAclName(); + request.aclIndex = standardAclContext.getIndex(aclName, mappingContext); + + getReplyForDelete(futureFacade.aclDel(request).toCompletableFuture(), id); + + // removes mapping after successful delete + standardAclContext.removeName(aclName, mappingContext); + } + + default void addMacIpAcl(@Nonnull final FutureJVppAclFacade futureFacade, + @Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl acl, + @Nonnull final NamingContext macIpAclContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + final MacipAclAdd request = new MacipAclAdd(); + + request.tag = getAclNameAsBytes(acl); + request.r = convertToMacIpAclRules(getAces(acl)); + request.count = request.r.length; + + final MacipAclAddReply reply = getReplyForWrite(futureFacade.macipAclAdd(request).toCompletableFuture(), id); + + // map mac-ip acl to returned index + macIpAclContext.addName(reply.aclIndex, acl.getAclName(), mappingContext); + } + + default void deleteMacIpAcl(@Nonnull final FutureJVppAclFacade futureFacade, + @Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl acl, + @Nonnull final NamingContext macIpAclContext, + @Nonnull final MappingContext mappingContext) throws WriteFailedException { + final MacipAclDel request = new MacipAclDel(); + final String aclName = acl.getAclName(); + request.aclIndex = macIpAclContext.getIndex(aclName, mappingContext); + + getReplyForDelete(futureFacade.macipAclDel(request).toCompletableFuture(), id); + + macIpAclContext.removeName(aclName, mappingContext); + } + + +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequest.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequest.java new file mode 100644 index 000000000..e3c5b727e --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequest.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package io.fd.hc2vpp.acl.util.iface.acl; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.List; +import java.util.stream.Stream; +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._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Multi-assignment single-request taking advantage from acl_interface_set_acl_list api + */ +public class AclInterfaceAssignmentRequest implements JvppReplyConsumer, ByteDataTranslator { + + private static final Logger LOG = LoggerFactory.getLogger(AclInterfaceAssignmentRequest.class); + + private final MappingContext mappingContext; + private InstanceIdentifier<Acl> identifier; + private List<String> inputAclNames; + private List<String> outputAclNames; + private NamingContext standardAclContext; + private NamingContext interfaceContext; + + + private AclInterfaceAssignmentRequest(final MappingContext mappingContext) { + this.mappingContext = checkNotNull(mappingContext, "Mapping context cannot be null"); + } + + public static AclInterfaceAssignmentRequest create(@Nonnull final MappingContext mappingContext) { + return new AclInterfaceAssignmentRequest(mappingContext); + } + + public AclInterfaceAssignmentRequest identifier( + @Nonnull final InstanceIdentifier<Acl> identifier) { + this.identifier = identifier; + return this; + } + + public AclInterfaceAssignmentRequest inputAclNames(@Nonnull final List<String> inputAclNames) { + this.inputAclNames = inputAclNames; + return this; + } + + public AclInterfaceAssignmentRequest outputAclNames(@Nonnull final List<String> outputAclNames) { + this.outputAclNames = outputAclNames; + return this; + } + + public AclInterfaceAssignmentRequest standardAclContext(@Nonnull final NamingContext standardAclContext) { + this.standardAclContext = standardAclContext; + return this; + } + + public AclInterfaceAssignmentRequest interfaceContext(@Nonnull final NamingContext interfaceContext) { + this.interfaceContext = interfaceContext; + return this; + } + + private void checkValidRequest() { + checkNotNull(identifier, "Identifier cannot be null"); + checkNotNull(inputAclNames, "Input ACL names cannot be null"); + checkNotNull(outputAclNames, "Output ACL names cannot be null"); + checkNotNull(standardAclContext, "ACL context cannot be null"); + checkNotNull(interfaceContext, "Interface context cannot be null"); + } + + public void executeAsCreate(@Nonnull final FutureJVppAclFacade api) throws WriteFailedException { + checkValidRequest(); + final String interfaceName = identifier.firstKeyOf(Interface.class).getName(); + + // locking on mapping context, to prevent modifying of mappings (for both contexts) during binding/execution of request + synchronized (mappingContext) { + LOG.debug( + "Executing acl interface assignment write request for interface={}, input ACL's={},output ACL's={}", + interfaceName, inputAclNames, outputAclNames); + + getReplyForWrite(api.aclInterfaceSetAclList(createRequest(interfaceName)).toCompletableFuture(), + identifier); + LOG.debug( + "Acl interface assignment write request for interface={}, input ACL's={},output ACL's={} successfully passed", + interfaceName, inputAclNames, outputAclNames); + } + } + + public void executeAsUpdate(@Nonnull final FutureJVppAclFacade api, final Acl before, final Acl after) + throws WriteFailedException { + checkValidRequest(); + final String interfaceName = identifier.firstKeyOf(Interface.class).getName(); + + // locking on mapping context, to prevent modifying of mappings (for both contexts) during binding/execution of request + synchronized (mappingContext) { + LOG.debug( + "Executing acl interface assignment update request for interface={}, input ACL's={},output ACL's={}", + interfaceName, inputAclNames, outputAclNames); + + getReplyForUpdate(api.aclInterfaceSetAclList(createRequest(interfaceName)).toCompletableFuture(), + identifier, before, after); + LOG.debug( + "Acl interface assignment update request for interface={}, input ACL's={},output ACL's={} successfully passed", + interfaceName, inputAclNames, outputAclNames); + } + } + + public void executeAsDelete(@Nonnull final FutureJVppAclFacade api) throws WriteFailedException { + checkValidRequest(); + final String interfaceName = identifier.firstKeyOf(Interface.class).getName(); + + // locking on mapping context, to prevent modifying of mappings (for both contexts) during binding/execution of request + synchronized (mappingContext) { + LOG.debug( + "Executing acl interface assignment delete request for interface={}, input ACL's={},output ACL's={}", + interfaceName, inputAclNames, outputAclNames); + + getReplyForDelete(api.aclInterfaceSetAclList(createRequest(interfaceName)).toCompletableFuture(), + identifier); + LOG.debug( + "Acl interface assignment delete request for interface={}, input ACL's={},output ACL's={} successfully passed", + interfaceName, inputAclNames, outputAclNames); + } + } + + // synchronized on higher layer + private AclInterfaceSetAclList createRequest(final String interfaceName) { + + AclInterfaceSetAclList request = new AclInterfaceSetAclList(); + request.swIfIndex = interfaceContext.getIndex(interfaceName, mappingContext); + request.nInput = (byte) inputAclNames.size(); + request.count = (byte) (inputAclNames.size() + outputAclNames.size()); + request.acls = Stream.concat(inputAclNames.stream(), outputAclNames.stream()) + .mapToInt(aclName -> standardAclContext.getIndex(aclName, mappingContext)) + .toArray(); + return request; + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequest.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequest.java new file mode 100644 index 000000000..f8d726397 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequest.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package io.fd.hc2vpp.acl.util.iface.macip; + +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.MacipAclInterfaceAddDel; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +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._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class MacIpInterfaceAssignmentRequest implements ByteDataTranslator, JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(MacIpInterfaceAssignmentRequest.class); + + private final boolean isNew; + private final MappingContext mappingContext; + private InstanceIdentifier<VppMacipAcl> identifier; + private String aclName; + private NamingContext macIpAclContext; + private NamingContext interfaceContext; + + + private MacIpInterfaceAssignmentRequest(final boolean isNew, final MappingContext mappingContext) { + this.isNew = isNew; + this.mappingContext = checkNotNull(mappingContext, "Mapping context cannot be null"); + } + + public static MacIpInterfaceAssignmentRequest addNew(@Nonnull final MappingContext mappingContext) { + return new MacIpInterfaceAssignmentRequest(true, mappingContext); + } + + public static MacIpInterfaceAssignmentRequest deleteExisting(@Nonnull final MappingContext mappingContext) { + return new MacIpInterfaceAssignmentRequest(false, mappingContext); + } + + public MacIpInterfaceAssignmentRequest identifier(@Nonnull final InstanceIdentifier<VppMacipAcl> identifier) { + this.identifier = identifier; + return this; + } + + public MacIpInterfaceAssignmentRequest aclName(@Nonnull final String aclName) { + this.aclName = aclName; + return this; + } + + public MacIpInterfaceAssignmentRequest macIpAclContext(@Nonnull final NamingContext macIpAclContext) { + this.macIpAclContext = macIpAclContext; + return this; + } + + public MacIpInterfaceAssignmentRequest interfaceContext(@Nonnull final NamingContext interfaceContext) { + this.interfaceContext = interfaceContext; + return this; + } + + private void checkValidRequest() { + checkNotNull(identifier, "Identifier cannot be null"); + checkNotNull(aclName, "ACL name cannot be null"); + checkNotNull(macIpAclContext, "ACL context cannot be null"); + checkNotNull(interfaceContext, "Interface context cannot be null"); + } + + public void execute(@Nonnull final FutureJVppAclFacade api) + throws WriteFailedException { + + // locking on mapping context, to prevent modifying of mappings (for both contexts) during execution of request + synchronized (mappingContext) { + checkValidRequest(); + + final String interfaceName = identifier.firstKeyOf(Interface.class).getName(); + + MacipAclInterfaceAddDel request = new MacipAclInterfaceAddDel(); + request.isAdd = booleanToByte(isNew); + request.aclIndex = macIpAclContext.getIndex(aclName, mappingContext); + request.swIfIndex = + interfaceContext.getIndex(interfaceName, mappingContext); + + LOG.debug("Executing mac-ip interface assignment request for isNew={},aclName={},interfaceName={}", isNew, + aclName, interfaceName); + getReplyForWrite(api.macipAclInterfaceAddDel(request).toCompletableFuture(), identifier); + LOG.debug( + "Mac-ip interface assignment request for isNew={},aclName={},interfaceName={} successfully passed", + isNew, aclName, interfaceName); + } + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducer.java new file mode 100644 index 000000000..c1f9a40ff --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducer.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.protocol; + +import static com.google.common.base.Preconditions.checkArgument; +import static io.fd.hc2vpp.acl.util.protocol.ProtoPreBindRuleProducer.ProtocolPair.pair; + +import com.google.common.collect.ImmutableSet; +import io.fd.vpp.jvpp.acl.types.AclRule; +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.ValueRange; +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.VppAce; +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.vpp.ace.VppAceNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.IpProtocol; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.Icmp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.IcmpV6; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.Other; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.Tcp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.Udp; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.icmp.IcmpNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.icmp.v6.IcmpV6Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.tcp.TcpNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.udp.UdpNodes; + +/** + * Creates ACL rules pre-bind with protocol-related fields.<br> + * Support TCP, UDP, ICMP and ICMPv6 protocol numbers according to + * <a href="http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml"> this document </a> + */ +public interface ProtoPreBindRuleProducer { + + int ICMP_INDEX = 1; + int TCP_INDEX = 6; + int UDP_INDEX = 17; + int ICMPV6_INDEX = 58; + + Set<ProtocolPair> PROTOCOL_PAIRS = ImmutableSet.of(pair(Icmp.class, ICMP_INDEX), pair(Tcp.class, TCP_INDEX), + pair(Udp.class, UDP_INDEX), pair(IcmpV6.class, ICMPV6_INDEX)); + + class ProtocolPair { + private final Class<? extends IpProtocol> protocolClass; + private final int index; + + private ProtocolPair(final Class<? extends IpProtocol> protocolClass, final int index) { + this.protocolClass = protocolClass; + this.index = index; + } + + static ProtocolPair pair(@Nonnull final Class<? extends IpProtocol> protocolClass, @Nonnull final int index) { + return new ProtocolPair(protocolClass, index); + } + + boolean match(@Nonnull final Class<? extends IpProtocol> protocolClass) { + return this.protocolClass.isAssignableFrom(protocolClass); + } + + int getIndex() { + return this.index; + } + } + + static byte protocol(final IpProtocol ipProtocol) { + final Optional<ProtocolPair> optPair = PROTOCOL_PAIRS.stream() + .filter(protocolPair -> protocolPair.match(ipProtocol.getClass())) + .findAny(); + + if (!optPair.isPresent()) { + if (Other.class.isAssignableFrom(ipProtocol.getClass())) { + return Other.class.cast(ipProtocol).getOtherNodes().getProtocol().byteValue(); + } + + throw new IllegalArgumentException(String.format("Unsupported Protocol Type %s", ipProtocol.getClass())); + } + return (byte) optPair.get().getIndex(); + } + + static AclRule bindIcmpNodes(AclRule rule, VppAce ace) { + final VppAceNodes vppAceNodes = ace.getVppAceNodes(); + checkArgument(vppAceNodes.getIpProtocol() instanceof Icmp); + final IcmpNodes icmp = Icmp.class.cast(vppAceNodes.getIpProtocol()).getIcmpNodes(); + final ValueRange typesRange = icmp.getIcmpTypeRange(); + final ValueRange codesRange = icmp.getIcmpCodeRange(); + + rule.srcportOrIcmptypeFirst = typesRange.getFirst(); + rule.srcportOrIcmptypeLast = typesRange.getLast(); + rule.dstportOrIcmpcodeFirst = codesRange.getFirst(); + rule.dstportOrIcmpcodeLast = codesRange.getLast(); + + return rule; + } + + static AclRule bindIcmpv6Nodes(AclRule rule, VppAce ace) { + final VppAceNodes vppAceNodes = ace.getVppAceNodes(); + checkArgument(vppAceNodes.getIpProtocol() instanceof IcmpV6); + final IcmpV6Nodes icmpV6 = IcmpV6.class.cast(vppAceNodes.getIpProtocol()).getIcmpV6Nodes(); + final ValueRange typesRange = icmpV6.getIcmpTypeRange(); + final ValueRange codesRange = icmpV6.getIcmpCodeRange(); + + rule.srcportOrIcmptypeFirst = typesRange.getFirst(); + rule.srcportOrIcmptypeLast = typesRange.getLast(); + rule.dstportOrIcmpcodeFirst = codesRange.getFirst(); + rule.dstportOrIcmpcodeLast = codesRange.getLast(); + + return rule; + } + + + static AclRule bindTcpNodes(AclRule rule, VppAce ace) { + final VppAceNodes vppAceNodes = ace.getVppAceNodes(); + checkArgument(vppAceNodes.getIpProtocol() instanceof Tcp); + + final TcpNodes tcp = Tcp.class.cast(vppAceNodes.getIpProtocol()).getTcpNodes(); + final SourcePortRange sourcePortRange = tcp.getSourcePortRange(); + final DestinationPortRange destinationPortRange = tcp.getDestinationPortRange(); + + rule.srcportOrIcmptypeFirst = portNumber(sourcePortRange.getLowerPort()); + rule.srcportOrIcmptypeLast = portNumber(sourcePortRange.getUpperPort()); + rule.dstportOrIcmpcodeFirst = portNumber(destinationPortRange.getLowerPort()); + rule.dstportOrIcmpcodeLast = portNumber(destinationPortRange.getUpperPort()); + rule.tcpFlagsMask = tcp.getTcpFlagsMask().byteValue(); + rule.tcpFlagsValue = tcp.getTcpFlagsValue().byteValue(); + + return rule; + } + + static AclRule bindUdpNodes(AclRule rule, VppAce ace) { + final VppAceNodes vppAceNodes = ace.getVppAceNodes(); + checkArgument(vppAceNodes.getIpProtocol() instanceof Udp); + + final UdpNodes udp = Udp.class.cast(vppAceNodes.getIpProtocol()).getUdpNodes(); + final SourcePortRange sourcePortRange = udp.getSourcePortRange(); + final DestinationPortRange destinationPortRange = udp.getDestinationPortRange(); + + rule.srcportOrIcmptypeFirst = portNumber(sourcePortRange.getLowerPort()); + rule.srcportOrIcmptypeLast = portNumber(sourcePortRange.getUpperPort()); + rule.dstportOrIcmpcodeFirst = portNumber(destinationPortRange.getLowerPort()); + rule.dstportOrIcmpcodeLast = portNumber(destinationPortRange.getUpperPort()); + + return rule; + } + + static AclRule bindDefaultNodes(AclRule rule) { + rule.srcportOrIcmptypeFirst = 0; + rule.srcportOrIcmptypeLast = (short) 65535; + rule.dstportOrIcmpcodeFirst = 0; + rule.dstportOrIcmpcodeLast = (short) 65535; + rule.tcpFlagsValue = 0; + rule.tcpFlagsMask = 0; + return rule; + } + + static short portNumber(final PortNumber portNumber) { + return portNumber.getValue().shortValue(); + } + + default AclRule createPreBindRule(@Nonnull final VppAce vppAce) { + AclRule rule = new AclRule(); + + rule.proto = protocol(vppAce.getVppAceNodes().getIpProtocol()); + + switch (rule.proto) { + case ICMP_INDEX: { + return bindIcmpNodes(rule, vppAce); + } + + case TCP_INDEX: { + return bindTcpNodes(rule, vppAce); + } + + case UDP_INDEX: { + return bindUdpNodes(rule, vppAce); + } + + case ICMPV6_INDEX: { + return bindIcmpv6Nodes(rule, vppAce); + } + default: { + return bindDefaultNodes(rule); + } + } + + } + +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizer.java new file mode 100644 index 000000000..3a4fb0cf3 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizer.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write; + +import static java.util.stream.Collectors.toList; + +import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer; +import io.fd.hc2vpp.acl.util.iface.acl.AclInterfaceAssignmentRequest; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.Collections; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.VppAclsBaseAttributes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAcls; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Handles acl assignments(only standard ones, mac-ip have dedicated customizer) + */ +public class InterfaceAclCustomizer extends FutureJVppAclCustomizer implements WriterCustomizer<Acl> { + + private final NamingContext interfaceContext; + private final NamingContext standardAclContext; + + public InterfaceAclCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade, + @Nonnull final NamingContext interfaceContext, + @Nonnull final NamingContext standardAclContext) { + super(jVppAclFacade); + this.interfaceContext = interfaceContext; + this.standardAclContext = standardAclContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + AclInterfaceAssignmentRequest.create(writeContext.getMappingContext()) + .standardAclContext(standardAclContext) + .interfaceContext(interfaceContext) + .identifier(id) + .inputAclNames(getAclNames(dataAfter.getIngress())) + .outputAclNames(getAclNames(dataAfter.getEgress())) + .executeAsCreate(getjVppAclFacade()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + AclInterfaceAssignmentRequest.create(writeContext.getMappingContext()) + .standardAclContext(standardAclContext) + .interfaceContext(interfaceContext) + .identifier(id) + .inputAclNames(getAclNames(dataAfter.getIngress())) + .outputAclNames(getAclNames(dataAfter.getEgress())) + .executeAsUpdate(getjVppAclFacade(), dataBefore, dataAfter); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + AclInterfaceAssignmentRequest.create(writeContext.getMappingContext()) + .standardAclContext(standardAclContext) + .interfaceContext(interfaceContext) + .identifier(id) + .inputAclNames(getAclNames(dataBefore.getIngress())) + .outputAclNames(getAclNames(dataBefore.getEgress())) + .executeAsDelete(getjVppAclFacade()); + } + + private static List<String> getAclNames(@Nonnull final VppAclsBaseAttributes acls) { + if (acls == null || acls.getVppAcls() == null) { + return Collections.emptyList(); + } else { + return acls.getVppAcls().stream().map(VppAcls::getName).collect(toList()); + } + } + + +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclMacIpCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclMacIpCustomizer.java new file mode 100644 index 000000000..4b59c83f5 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/InterfaceAclMacIpCustomizer.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write; + +import static io.fd.hc2vpp.acl.util.iface.macip.MacIpInterfaceAssignmentRequest.addNew; +import static io.fd.hc2vpp.acl.util.iface.macip.MacIpInterfaceAssignmentRequest.deleteExisting; + +import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.spi.write.WriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + + +public class InterfaceAclMacIpCustomizer extends FutureJVppAclCustomizer implements WriterCustomizer<VppMacipAcl> { + + private final NamingContext macIpAclContext; + private final NamingContext interfaceContext; + + public InterfaceAclMacIpCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade, + @Nonnull final NamingContext macIpAclContext, + @Nonnull final NamingContext interfaceContext) { + super(jVppAclFacade); + this.macIpAclContext = macIpAclContext; + this.interfaceContext = interfaceContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<VppMacipAcl> id, + @Nonnull final VppMacipAcl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + addNew(writeContext.getMappingContext()) + .identifier(id) + .aclName(dataAfter.getName()) + .macIpAclContext(macIpAclContext) + .interfaceContext(interfaceContext) + .execute(getjVppAclFacade()); + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<VppMacipAcl> id, + @Nonnull final VppMacipAcl dataBefore, + @Nonnull final VppMacipAcl dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter, + new UnsupportedOperationException("Operation not supported")); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<VppMacipAcl> id, + @Nonnull final VppMacipAcl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + deleteExisting(writeContext.getMappingContext()) + .identifier(id) + .aclName(dataBefore.getName()) + .macIpAclContext(macIpAclContext) + .interfaceContext(interfaceContext) + .execute(getjVppAclFacade()); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java new file mode 100644 index 000000000..a2956784f --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/VppAclCustomizer.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write; + +import io.fd.hc2vpp.acl.util.FutureJVppAclCustomizer; +import io.fd.hc2vpp.acl.util.acl.AclDataExtractor; +import io.fd.hc2vpp.acl.util.acl.AclValidator; +import io.fd.hc2vpp.acl.util.acl.AclWriter; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import javax.annotation.Nonnull; +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.yangtools.yang.binding.InstanceIdentifier; + +public class VppAclCustomizer extends FutureJVppAclCustomizer + implements ListWriterCustomizer<Acl, AclKey>, AclValidator, AclDataExtractor, AclWriter { + + private final NamingContext standardAclContext; + private final NamingContext macIpAclContext; + + public VppAclCustomizer(@Nonnull final FutureJVppAclFacade jVppAclFacade, + @Nonnull final NamingContext standardAclContext, + @Nonnull final NamingContext macIpAclContext) { + super(jVppAclFacade); + this.standardAclContext = standardAclContext; + this.macIpAclContext = macIpAclContext; + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + validateAcl(dataAfter); + + final MappingContext mappingContext = writeContext.getMappingContext(); + + if (isStandardAcl(dataAfter)) { + addStandardAcl(getjVppAclFacade(), id, dataAfter, standardAclContext, mappingContext); + } else if (isMacIpAcl(dataAfter)) { + addMacIpAcl(getjVppAclFacade(), id, dataAfter, macIpAclContext, mappingContext); + } else { + // double check, first one done by validation + throw new WriteFailedException.CreateFailedException(id, dataAfter, + new IllegalArgumentException("Unsupported acl option")); + } + } + + @Override + public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final Acl dataAfter, @Nonnull final WriteContext writeContext) + throws WriteFailedException { + validateAcl(dataAfter); + + final MappingContext mappingContext = writeContext.getMappingContext(); + + if (isStandardAcl(dataAfter)) { + updateStandardAcl(getjVppAclFacade(), id, dataAfter, standardAclContext, mappingContext); + } else if (isMacIpAcl(dataAfter)) { + synchronized (macIpAclContext) { + // there is no direct support for update of mac-ip acl, but only one is allowed per interface + // so it is atomic from vpp standpoint. Enclosed in synchronized block to prevent issues with + // multiple threads managing naming context + deleteMacIpAcl(getjVppAclFacade(), id, dataBefore, macIpAclContext, mappingContext); + addMacIpAcl(getjVppAclFacade(), id, dataAfter, macIpAclContext, mappingContext); + } + } else { + // double check, first one done by validation + throw new WriteFailedException.CreateFailedException(id, dataAfter, + new IllegalArgumentException("Unsupported acl option")); + } + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<Acl> id, @Nonnull final Acl dataBefore, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + // According to VPP team, acl references should be removed before trying to remove ACL + // For mac-ip, reference should be removed during removal of mac-ip, so no need to check in hc + validateAcl(dataBefore); + + final MappingContext mappingContext = writeContext.getMappingContext(); + + if (isStandardAcl(dataBefore)) { + deleteStandardAcl(getjVppAclFacade(), id, dataBefore, standardAclContext, mappingContext); + } else if (isMacIpAcl(dataBefore)) { + deleteMacIpAcl(getjVppAclFacade(), id, dataBefore, macIpAclContext, mappingContext); + } else { + // double check, first one done by validation + throw new WriteFailedException.DeleteFailedException(id, + new IllegalArgumentException("Unsupported acl option")); + } + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/AbstractAclWriterFactory.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/AbstractAclWriterFactory.java new file mode 100644 index 000000000..fe22f2fd7 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/AbstractAclWriterFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write.factory; + +import com.google.inject.Inject; +import com.google.inject.name.Named; +import io.fd.hc2vpp.acl.AclModule; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; + +/** + * Created by jsrnicek on 12.12.2016. + */ +class AbstractAclWriterFactory { + + @Inject + protected FutureJVppAclFacade futureAclFacade; + + @Inject + @Named(AclModule.STANDARD_ACL_CONTEXT_NAME) + protected NamingContext standardAclContext; + + @Inject + @Named(AclModule.MAC_IP_ACL_CONTEXT_NAME) + protected NamingContext macIpAClContext; + + @Inject + @Named("interface-context") + protected NamingContext interfaceContext; +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/InterfaceAclWriterFactory.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/InterfaceAclWriterFactory.java new file mode 100644 index 000000000..6598aae9c --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/InterfaceAclWriterFactory.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write.factory; + +import com.google.common.collect.ImmutableSet; +import io.fd.hc2vpp.acl.write.InterfaceAclCustomizer; +import io.fd.hc2vpp.acl.write.InterfaceAclMacIpCustomizer; +import io.fd.honeycomb.translate.impl.write.GenericWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import java.util.Set; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +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._interface.acl.rev161214.VppAclInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Egress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.acls.base.attributes.VppAcls; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class InterfaceAclWriterFactory extends AbstractAclWriterFactory implements WriterFactory { + + private static final InstanceIdentifier<Acl> ACL_IID = + InstanceIdentifier.create(Interfaces.class).child(Interface.class) + .augmentation(VppAclInterfaceAugmentation.class).child(Acl.class); + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + registry.subtreeAdd(aclHandledChildren(InstanceIdentifier.create(Acl.class)), + new GenericWriter<>(ACL_IID, + new InterfaceAclCustomizer(futureAclFacade, interfaceContext, standardAclContext))); + + registry.add(new GenericWriter<>(ACL_IID.child(Ingress.class).child(VppMacipAcl.class), + new InterfaceAclMacIpCustomizer(futureAclFacade, macIpAClContext, interfaceContext))); + } + + private Set<InstanceIdentifier<?>> aclHandledChildren(final InstanceIdentifier<Acl> parentId) { + return ImmutableSet.of(parentId.child(Ingress.class), + parentId.child(Ingress.class).child(VppAcls.class), + parentId.child(Egress.class), + parentId.child(Egress.class).child(VppAcls.class)); + } +} diff --git a/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java new file mode 100644 index 000000000..546924ee3 --- /dev/null +++ b/acl/acl-impl/src/main/java/io/fd/hc2vpp/acl/write/factory/VppAclWriterFactory.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write.factory; + +import com.google.common.collect.ImmutableSet; +import io.fd.hc2vpp.acl.write.VppAclCustomizer; +import io.fd.honeycomb.translate.impl.write.GenericListWriter; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.honeycomb.translate.write.registry.ModifiableWriterRegistryBuilder; +import java.util.Set; +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.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.DestinationPortRange; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.acl.transport.header.fields.SourcePortRange; +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.vpp.ace.VppAceNodes; +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.vpp.macip.ace.VppMacipAceNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.icmp.header.fields.IcmpCodeRange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.icmp.header.fields.IcmpTypeRange; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.icmp.IcmpNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.icmp.v6.IcmpV6Nodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.other.OtherNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.tcp.TcpNodes; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.acl.ip.protocol.header.fields.ip.protocol.udp.UdpNodes; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class VppAclWriterFactory extends AbstractAclWriterFactory implements WriterFactory { + + private static Set<InstanceIdentifier<?>> vppAclCustomizerHandledChildren(final InstanceIdentifier<Acl> parentId) { + final InstanceIdentifier<Matches> matchesIid = + parentId.child(AccessListEntries.class).child(Ace.class).child(Matches.class); + return ImmutableSet.of(parentId.child(AccessListEntries.class), + parentId.child(AccessListEntries.class).child(Ace.class), + parentId.child(AccessListEntries.class).child(Ace.class).child(Matches.class), + parentId.child(AccessListEntries.class).child(Ace.class).child(Actions.class), + matchesIid, + matchesIid.child(VppMacipAceNodes.class), + matchesIid.child(VppAceNodes.class), + matchesIid.child(VppAceNodes.class).child(IcmpNodes.class), + matchesIid.child(VppAceNodes.class).child(IcmpNodes.class).child(IcmpCodeRange.class), + matchesIid.child(VppAceNodes.class).child(IcmpNodes.class).child(IcmpTypeRange.class), + matchesIid.child(VppAceNodes.class).child(IcmpV6Nodes.class), + matchesIid.child(VppAceNodes.class).child(IcmpV6Nodes.class).child(IcmpCodeRange.class), + matchesIid.child(VppAceNodes.class).child(IcmpV6Nodes.class).child(IcmpTypeRange.class), + matchesIid.child(VppAceNodes.class).child(UdpNodes.class), + matchesIid.child(VppAceNodes.class).child(UdpNodes.class).child(SourcePortRange.class), + matchesIid.child(VppAceNodes.class).child(UdpNodes.class).child(DestinationPortRange.class), + matchesIid.child(VppAceNodes.class).child(TcpNodes.class), + matchesIid.child(VppAceNodes.class).child(TcpNodes.class).child(SourcePortRange.class), + matchesIid.child(VppAceNodes.class).child(TcpNodes.class).child(DestinationPortRange.class), + matchesIid.child(VppAceNodes.class).child(OtherNodes.class) + + ); + } + + @Override + public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { + final InstanceIdentifier<AccessLists> rootNode = InstanceIdentifier.create(AccessLists.class); + + registry.subtreeAdd(vppAclCustomizerHandledChildren(InstanceIdentifier.create(Acl.class)), + new GenericListWriter<>(rootNode.child(Acl.class), + new VppAclCustomizer(futureAclFacade, standardAclContext, macIpAClContext))); + } +} diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclModuleTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclModuleTest.java new file mode 100644 index 000000000..671cd283c --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclModuleTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.Matchers.empty; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Provider; +import com.google.inject.multibindings.Multibinder; +import com.google.inject.name.Named; +import com.google.inject.testing.fieldbinder.Bind; +import com.google.inject.testing.fieldbinder.BoundFieldModule; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.impl.read.registry.CompositeReaderRegistryBuilder; +import io.fd.honeycomb.translate.impl.write.registry.FlatWriterRegistryBuilder; +import io.fd.honeycomb.translate.read.ReaderFactory; +import io.fd.honeycomb.translate.write.WriterFactory; +import io.fd.vpp.jvpp.JVppRegistry; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.HashSet; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.controller.md.sal.binding.api.DataBroker; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesState; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.InterfacesStateBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.Interface; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.interfaces.state.InterfaceBuilder; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + + +public class AclModuleTest { + + @Named("honeycomb-context") + @Bind + @Mock + private DataBroker honeycombContext; + + @Named("honeycomb-initializer") + @Bind + @Mock + private DataBroker honeycombInitializer; + + @Named("interface-context") + @Bind + private NamingContext interfaceContext = new NamingContext("iface", "interface-context"); + + @Bind + @Mock + private JVppRegistry registry; + + @Inject + private Set<WriterFactory> writerFactories = new HashSet<>(); + + @Inject + private Set<ReaderFactory> readerFactories = new HashSet<>(); + + @Before + public void setUp() throws Exception { + initMocks(this); + // AclModule adds readers under InterfacesState + // because readers for parents need to be present (are part of V3poModule) + // add structural readers here (better than maven dependency). + + Guice.createInjector(binder -> Multibinder.newSetBinder(binder, ReaderFactory.class) + .addBinding().toInstance(registry -> { + registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class), + InterfacesStateBuilder.class); + registry.addStructuralReader(InstanceIdentifier.create(InterfacesState.class).child(Interface.class), + InterfaceBuilder.class); + }), new AclModule(MockJVppAclProvider.class), BoundFieldModule.of(this)).injectMembers(this); + } + + @Test + public void testWriterFactories() throws Exception { + assertThat(writerFactories, is(not(empty()))); + + final FlatWriterRegistryBuilder registryBuilder = new FlatWriterRegistryBuilder(); + writerFactories.forEach(factory -> factory.init(registryBuilder)); + assertNotNull(registryBuilder.build()); + } + + @Test + public void testReaderFactories() throws Exception { + assertThat(readerFactories, is(not(empty()))); + + final CompositeReaderRegistryBuilder registryBuilder = new CompositeReaderRegistryBuilder(); + readerFactories.forEach(factory -> factory.init(registryBuilder)); + assertNotNull(registryBuilder.build()); + } + + private static final class MockJVppAclProvider implements Provider<FutureJVppAclFacade> { + + @Override + public FutureJVppAclFacade get() { + return mock(FutureJVppAclFacade.class); + } + } +} + diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclTestSchemaContext.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclTestSchemaContext.java new file mode 100644 index 000000000..dd3c1caa9 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/AclTestSchemaContext.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl; + +import com.google.common.collect.ImmutableSet; +import io.fd.honeycomb.test.tools.annotations.SchemaContextProvider; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; + + +public interface AclTestSchemaContext { + + @SchemaContextProvider + default ModuleInfoBackedContext context() { + ModuleInfoBackedContext context = ModuleInfoBackedContext.create(); + context.addModuleInfos(ImmutableSet + .of(org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.packet.fields.rev160708.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.access.control.list.rev160708.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.$YangModuleInfoImpl + .getInstance(), + org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.$YangModuleInfoImpl + .getInstance())); + return context; + } +} diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequestTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequestTest.java new file mode 100644 index 000000000..2224d11b0 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/acl/AclInterfaceAssignmentRequestTest.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.iface.acl; + +import static io.fd.hc2vpp.acl.util.iface.acl.AclInterfaceAssignmentRequest.create; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import com.google.common.collect.ImmutableList; +import io.fd.hc2vpp.common.test.util.FutureProducer; +import io.fd.hc2vpp.common.test.util.NamingContextHelper; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclList; +import io.fd.vpp.jvpp.acl.dto.AclInterfaceSetAclListReply; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import java.util.Arrays; +import java.util.Collections; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +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._interface.acl.rev161214.VppAclInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class AclInterfaceAssignmentRequestTest implements NamingContextHelper, FutureProducer { + + private static final String INTERFACE_NAME = "iface"; + private static final int INTERFACE_INDEX = 4; + + private static final String ACL_NAME_1 = "acl-1"; + private static final String ACL_NAME_2 = "acl-2"; + private static final String ACL_NAME_3 = "acl-3"; + private static final String ACL_NAME_4 = "acl-4"; + private static final String ACL_NAME_5 = "acl-5"; + + private static final int ACL_INDEX_1 = 2; + private static final int ACL_INDEX_2 = 7; + private static final int ACL_INDEX_3 = 4; + private static final int ACL_INDEX_4 = 5; + private static final int ACL_INDEX_5 = 1; + + @Captor + private ArgumentCaptor<AclInterfaceSetAclList> requestCaptor; + + @Mock + private FutureJVppAclFacade api; + + @Mock + private MappingContext mappingContext; + + private InstanceIdentifier<Acl> validIdentifier; + private NamingContext interfaceContext; + private NamingContext aclContext; + + @Before + public void setUp() { + initMocks(this); + + validIdentifier = InstanceIdentifier.create(Interfaces.class). + child(Interface.class, new InterfaceKey(INTERFACE_NAME)) + .augmentation(VppAclInterfaceAugmentation.class) + .child(Acl.class); + + interfaceContext = new NamingContext("iface", "interface-context"); + aclContext = new NamingContext("acl", "acl-context"); + + defineMapping(mappingContext, INTERFACE_NAME, INTERFACE_INDEX, "interface-context"); + + defineMapping(mappingContext, ACL_NAME_1, ACL_INDEX_1, "acl-context"); + defineMapping(mappingContext, ACL_NAME_2, ACL_INDEX_2, "acl-context"); + defineMapping(mappingContext, ACL_NAME_3, ACL_INDEX_3, "acl-context"); + defineMapping(mappingContext, ACL_NAME_4, ACL_INDEX_4, "acl-context"); + defineMapping(mappingContext, ACL_NAME_5, ACL_INDEX_5, "acl-context"); + + when(api.aclInterfaceSetAclList(any())).thenReturn(future(new AclInterfaceSetAclListReply())); + } + + @Test + public void verifyNegativeCases() throws WriteFailedException { + verifyVariant(create(mappingContext)); + + verifyVariant(create(mappingContext).identifier(validIdentifier)); + + verifyVariant(create(mappingContext).identifier(validIdentifier).interfaceContext(interfaceContext)); + + verifyVariant(create(mappingContext).identifier(validIdentifier).interfaceContext(interfaceContext) + .standardAclContext(aclContext)); + + verifyVariant(create(mappingContext).identifier(validIdentifier).interfaceContext(interfaceContext) + .standardAclContext(aclContext).inputAclNames(Collections.emptyList())); + + verifyVariant(create(mappingContext).identifier(validIdentifier).interfaceContext(interfaceContext) + .standardAclContext(aclContext).outputAclNames(Collections.emptyList())); + } + + private void verifyVariant(final AclInterfaceAssignmentRequest request) throws WriteFailedException { + verifyCreateFailsWithNullPointer(request); + verifyUpdateFailsWithNullPointer(request); + verifyDeleteFailsWithNullPointer(request); + } + + @Test + public void executeAsCreate() throws Exception { + + createValidRequest().executeAsCreate(api); + createValidRequest().executeAsUpdate(api, mock(Acl.class), mock(Acl.class)); + createValidRequest().executeAsDelete(api); + + verify(api, times(3)).aclInterfaceSetAclList(requestCaptor.capture()); + requestCaptor.getAllValues().forEach(AclInterfaceAssignmentRequestTest::verifyValidRequest); + } + + private AclInterfaceAssignmentRequest createValidRequest() { + return create(mappingContext) + .identifier(validIdentifier) + .inputAclNames(ImmutableList.of(ACL_NAME_1, ACL_NAME_2, ACL_NAME_3)) + .outputAclNames(ImmutableList.of(ACL_NAME_4, ACL_NAME_5)) + .standardAclContext(aclContext) + .interfaceContext(interfaceContext); + } + + private static void verifyValidRequest(final AclInterfaceSetAclList request) { + assertNotNull(request); + assertEquals(5, request.count); + assertEquals(3, request.nInput); + assertTrue(Arrays.equals(new int[]{ACL_INDEX_1, ACL_INDEX_2, ACL_INDEX_3, ACL_INDEX_4, ACL_INDEX_5}, + request.acls)); + } + + private void verifyCreateFailsWithNullPointer(final AclInterfaceAssignmentRequest request) + throws WriteFailedException { + try { + request.executeAsCreate(api); + } catch (NullPointerException e) { + return; + } + fail("Test should have thrown null pointer"); + } + + private void verifyUpdateFailsWithNullPointer(final AclInterfaceAssignmentRequest request) + throws WriteFailedException { + try { + request.executeAsUpdate(api, mock(Acl.class), mock(Acl.class)); + } catch (NullPointerException e) { + return; + } + fail("Test should have thrown null pointer"); + } + + private void verifyDeleteFailsWithNullPointer(final AclInterfaceAssignmentRequest request) + throws WriteFailedException { + try { + request.executeAsCreate(api); + } catch (NullPointerException e) { + return; + } + fail("Test should have thrown null pointer"); + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequestTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequestTest.java new file mode 100644 index 000000000..e81550390 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/iface/macip/MacIpInterfaceAssignmentRequestTest.java @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.iface.macip; + +import static io.fd.hc2vpp.acl.util.iface.macip.MacIpInterfaceAssignmentRequest.addNew; +import static io.fd.hc2vpp.acl.util.iface.macip.MacIpInterfaceAssignmentRequest.deleteExisting; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.hc2vpp.common.test.util.FutureProducer; +import io.fd.hc2vpp.common.test.util.NamingContextHelper; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.translate.MappingContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.MacipAclInterfaceAddDel; +import io.fd.vpp.jvpp.acl.dto.MacipAclInterfaceAddDelReply; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.interfaces.rev140508.Interfaces; +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._interface.acl.rev161214.VppAclInterfaceAugmentation; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.Acl; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214._interface.acl.attributes.acl.Ingress; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang._interface.acl.rev161214.vpp.macip.acls.base.attributes.VppMacipAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +/** + * Created by jsrnicek on 13.12.2016. + */ +public class MacIpInterfaceAssignmentRequestTest implements NamingContextHelper,FutureProducer { + + private static final String INTERFACE_NAME = "iface"; + private static final int INTERFACE_INDEX = 4; + private static final String ACL_NAME = "mac-ip-acl"; + private static final int ACL_INDEX = 5; + + @Captor + private ArgumentCaptor<MacipAclInterfaceAddDel> requestCaptor; + + @Mock + private FutureJVppAclFacade api; + + @Mock + private MappingContext mappingContext; + + private InstanceIdentifier<VppMacipAcl> validIdentifier; + private NamingContext interfaceContext; + private NamingContext macIpAclContext; + + @Before + public void setUp() throws Exception { + initMocks(this); + + validIdentifier = InstanceIdentifier.create(Interfaces.class). + child(Interface.class, new InterfaceKey(INTERFACE_NAME)) + .augmentation(VppAclInterfaceAugmentation.class) + .child(Acl.class) + .child(Ingress.class) + .child(VppMacipAcl.class); + + interfaceContext = new NamingContext("iface", "interface-context"); + macIpAclContext = new NamingContext("mac-ip-acl", "mac-ip-acl-context"); + + defineMapping(mappingContext, INTERFACE_NAME, INTERFACE_INDEX, "interface-context"); + defineMapping(mappingContext, ACL_NAME, ACL_INDEX, "mac-ip-acl-context"); + when(api.macipAclInterfaceAddDel(any())).thenReturn(future(new MacipAclInterfaceAddDelReply())); + } + + @Test + public void testAddNew() throws Exception { + addNew(mappingContext) + .aclName(ACL_NAME) + .identifier(validIdentifier) + .interfaceContext(interfaceContext) + .macIpAclContext(macIpAclContext) + .execute(api); + verify(api, times(1)).macipAclInterfaceAddDel(requestCaptor.capture()); + + final MacipAclInterfaceAddDel request = requestCaptor.getValue(); + + assertNotNull(request); + assertEquals(1, request.isAdd); + assertEquals(INTERFACE_INDEX, request.swIfIndex); + assertEquals(ACL_INDEX, request.aclIndex); + } + + @Test + public void testDeleteExisting() throws Exception { + deleteExisting(mappingContext) + .aclName(ACL_NAME) + .identifier(validIdentifier) + .interfaceContext(interfaceContext) + .macIpAclContext(macIpAclContext) + .execute(api); + verify(api, times(1)).macipAclInterfaceAddDel(requestCaptor.capture()); + + final MacipAclInterfaceAddDel request = requestCaptor.getValue(); + + assertNotNull(request); + assertEquals(0, request.isAdd); + assertEquals(INTERFACE_INDEX, request.swIfIndex); + assertEquals(ACL_INDEX, request.aclIndex); + } + + @Test + public void testInvalidCases() throws Exception { + verifyFailsWithNullPointer(addNew(mappingContext)); + verifyFailsWithNullPointer(addNew(mappingContext).aclName(ACL_NAME)); + verifyFailsWithNullPointer(addNew(mappingContext).aclName(ACL_NAME).interfaceContext(interfaceContext)); + verifyFailsWithNullPointer(addNew(mappingContext).aclName(ACL_NAME).interfaceContext(interfaceContext) + .macIpAclContext(macIpAclContext)); + verifyFailsWithNullPointer(addNew(mappingContext).aclName(ACL_NAME).interfaceContext(interfaceContext) + .identifier(validIdentifier)); + } + + private void verifyFailsWithNullPointer(final MacIpInterfaceAssignmentRequest request) throws WriteFailedException { + try { + request.execute(api); + } catch (NullPointerException e) { + return; + } + fail("Test should have thrown null pointer"); + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducerTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducerTest.java new file mode 100644 index 000000000..8f4100349 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/util/protocol/ProtoPreBindRuleProducerTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.util.protocol; + +import static org.junit.Assert.assertEquals; + +import io.fd.hc2vpp.acl.AclTestSchemaContext; +import io.fd.honeycomb.test.tools.HoneycombTestRunner; +import io.fd.honeycomb.test.tools.annotations.InjectTestData; +import io.fd.vpp.jvpp.acl.types.AclRule; +import org.junit.Test; +import org.junit.runner.RunWith; +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.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.access.lists.acl.access.list.entries.ace.matches.ace.type.VppAce; + +@RunWith(HoneycombTestRunner.class) +public class ProtoPreBindRuleProducerTest implements ProtoPreBindRuleProducer, AclTestSchemaContext { + + //TODO - remove after resolving how to address identity from different model in textual yang instance identifier + private VppAce extractAce(AccessLists accessLists) { + return VppAce.class + .cast(accessLists.getAcl().get(0).getAccessListEntries().getAce().get(0).getMatches().getAceType()); + } + + @Test + public void testIcmpRule(@InjectTestData(resourcePath = "/rules/icmp-rule.json") AccessLists acls) { + final AclRule icmpRule = createPreBindRule(extractAce(acls)); + + assertEquals(1, icmpRule.proto); + assertEquals(5, icmpRule.srcportOrIcmptypeFirst); + assertEquals(8, icmpRule.srcportOrIcmptypeLast); + assertEquals(1, icmpRule.dstportOrIcmpcodeFirst); + assertEquals(3, icmpRule.dstportOrIcmpcodeLast); + assertEquals(0, icmpRule.tcpFlagsMask); + assertEquals(0, icmpRule.tcpFlagsValue); + } + + + @Test + public void testIcmpv6Rule(@InjectTestData(resourcePath = "/rules/icmp-v6-rule.json") AccessLists acls) { + final AclRule icmpv6Rule = createPreBindRule(extractAce(acls)); + + assertEquals(58, icmpv6Rule.proto); + assertEquals(5, icmpv6Rule.srcportOrIcmptypeFirst); + assertEquals(8, icmpv6Rule.srcportOrIcmptypeLast); + assertEquals(1, icmpv6Rule.dstportOrIcmpcodeFirst); + assertEquals(3, icmpv6Rule.dstportOrIcmpcodeLast); + assertEquals(0, icmpv6Rule.tcpFlagsMask); + assertEquals(0, icmpv6Rule.tcpFlagsValue); + } + + @Test + public void testTcpRule(@InjectTestData(resourcePath = "/rules/tcp-rule.json") AccessLists acls) { + final AclRule tcpRule = createPreBindRule(extractAce(acls)); + assertEquals(6, tcpRule.proto); + assertEquals(1, tcpRule.srcportOrIcmptypeFirst); + assertEquals(5487, tcpRule.srcportOrIcmptypeLast); + assertEquals(87, tcpRule.dstportOrIcmpcodeFirst); + assertEquals(6745, tcpRule.dstportOrIcmpcodeLast); + assertEquals(1, tcpRule.tcpFlagsMask); + assertEquals(7, tcpRule.tcpFlagsValue); + } + + @Test + public void testUdpRule(@InjectTestData(resourcePath = "/rules/udp-rule.json") AccessLists acls) { + final AclRule udpRule = createPreBindRule(extractAce(acls)); + assertEquals(17, udpRule.proto); + assertEquals(1, udpRule.srcportOrIcmptypeFirst); + assertEquals(5487, udpRule.srcportOrIcmptypeLast); + assertEquals(87, udpRule.dstportOrIcmpcodeFirst); + assertEquals(6745, udpRule.dstportOrIcmpcodeLast); + assertEquals(0, udpRule.tcpFlagsMask); + assertEquals(0, udpRule.tcpFlagsValue); + } + + @Test + public void testOtherRule(@InjectTestData(resourcePath = "/rules/other-rule.json") AccessLists acls) { + final AclRule icmpRule = createPreBindRule(extractAce(acls)); + assertEquals(64, icmpRule.proto); + assertEquals(0, icmpRule.srcportOrIcmptypeFirst); + assertEquals((short) 65535, icmpRule.srcportOrIcmptypeLast); + assertEquals(0, icmpRule.dstportOrIcmpcodeFirst); + assertEquals((short) 65535, icmpRule.dstportOrIcmpcodeLast); + assertEquals(0, icmpRule.tcpFlagsMask); + assertEquals(0, icmpRule.tcpFlagsValue); + } + +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizerTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizerTest.java new file mode 100644 index 000000000..cd261970e --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/InterfaceAclCustomizerTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write; + +import io.fd.hc2vpp.acl.AclTestSchemaContext; +import io.fd.hc2vpp.common.test.write.WriterCustomizerTest; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import org.junit.Test; +import org.mockito.Mock; + +public class InterfaceAclCustomizerTest extends WriterCustomizerTest implements AclTestSchemaContext { + + @Mock + private FutureJVppAclFacade aclApi; + + @Override + protected void setUpTest() throws Exception { + + } + + @Test + public void writeCurrentAttributes() throws Exception { + + } + + @Test + public void updateCurrentAttributes() throws Exception { + + } + + @Test + public void deleteCurrentAttributes() throws Exception { + + } + +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java new file mode 100644 index 000000000..d076311c9 --- /dev/null +++ b/acl/acl-impl/src/test/java/io/fd/hc2vpp/acl/write/VppAclCustomizerTest.java @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2016 Cisco and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.fd.hc2vpp.acl.write; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.fd.hc2vpp.acl.AclTestSchemaContext; +import io.fd.hc2vpp.common.test.write.WriterCustomizerTest; +import io.fd.hc2vpp.common.translate.util.NamingContext; +import io.fd.honeycomb.test.tools.HoneycombTestRunner; +import io.fd.honeycomb.test.tools.annotations.InjectTestData; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.acl.dto.AclAddReplace; +import io.fd.vpp.jvpp.acl.dto.AclAddReplaceReply; +import io.fd.vpp.jvpp.acl.dto.AclDel; +import io.fd.vpp.jvpp.acl.dto.AclDelReply; +import io.fd.vpp.jvpp.acl.dto.MacipAclAdd; +import io.fd.vpp.jvpp.acl.dto.MacipAclAddReply; +import io.fd.vpp.jvpp.acl.future.FutureJVppAclFacade; +import io.fd.vpp.jvpp.acl.types.AclRule; +import io.fd.vpp.jvpp.acl.types.MacipAclRule; +import java.util.Arrays; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +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.AclKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.acl.rev161214.VppAcl; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +@RunWith(HoneycombTestRunner.class) +public class VppAclCustomizerTest extends WriterCustomizerTest implements AclTestSchemaContext { + + @Mock + private FutureJVppAclFacade aclApi; + + @Captor + private ArgumentCaptor<AclAddReplace> aclAddReplaceRequestCaptor; + + @Captor + private ArgumentCaptor<MacipAclAdd> macipAclAddReplaceRequestCaptor; + + @Captor + private ArgumentCaptor<AclDel> aclDelRequestCaptor; + + private InstanceIdentifier<Acl> validId; + private InstanceIdentifier<Acl> validMacipId; + private VppAclCustomizer aclCustomizer; + private NamingContext standardAclContext; + private NamingContext macIpAclContext; + + + @Override + protected void setUpTest() throws Exception { + validId = + InstanceIdentifier.create(AccessLists.class).child(Acl.class, new AclKey("standard-acl", VppAcl.class)); + validMacipId = + InstanceIdentifier.create(AccessLists.class).child(Acl.class, new AclKey("macip-acl", VppAcl.class)); + standardAclContext = new NamingContext("acl", "acl-context"); + macIpAclContext = new NamingContext("macip", "macip-context"); + aclCustomizer = new VppAclCustomizer(aclApi, standardAclContext, macIpAclContext); + + when(aclApi.aclAddReplace(any())).thenReturn(future(new AclAddReplaceReply())); + when(aclApi.aclDel(any())).thenReturn(future(new AclDelReply())); + when(aclApi.macipAclAdd(any())).thenReturn(future(new MacipAclAddReply())); + } + + @Test + public void writeCurrentAttributesMacip(@InjectTestData(resourcePath = "/acl/macip/macip-acl.json") + AccessLists macipAcl) throws WriteFailedException { + + aclCustomizer.writeCurrentAttributes(validMacipId, macipAcl.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).macipAclAdd(macipAclAddReplaceRequestCaptor.capture()); + + final MacipAclAdd request = macipAclAddReplaceRequestCaptor.getValue(); + + assertEquals(1, request.count); + assertTrue(Arrays.equals("macip-acl".getBytes(), request.tag)); + + final MacipAclRule rule = request.r[0]; + + assertEquals(0, rule.isIpv6); + assertEquals(1, rule.isPermit); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 2}, rule.srcIpAddr)); + assertEquals(32, rule.srcIpPrefixLen); + assertTrue(Arrays.equals(new byte[]{(byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa, (byte)0xaa}, rule.srcMac)); + assertTrue(Arrays.equals(new byte[]{(byte)0xff, 0, 0, 0, 0, 0}, rule.srcMacMask)); + } + + @Test + public void writeCurrentAttributesIcmpIpv4(@InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") + AccessLists standardAcls) throws Exception { + aclCustomizer.writeCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyIcmpIpv4Request(-1); + } + + @Test + public void updateCurrentAttributesIcmpIpv4(@InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + final Acl data = standardAcls.getAcl().get(0); + + aclCustomizer.updateCurrentAttributes(validId, data, data, writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyIcmpIpv4Request(4); + } + + + @Test + public void writeCurrentAttributesIcmpIpv6(@InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp-v6.json") + AccessLists standardAcls) throws Exception { + aclCustomizer.writeCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyIcmpv6Request(-1); + } + + @Test + public void updateCurrentAttributesIcmpIpv6( + @InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp-v6.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + final Acl data = standardAcls.getAcl().get(0); + aclCustomizer.updateCurrentAttributes(validId, data, data, writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyIcmpv6Request(4); + } + + + @Test + public void writeCurrentAttributesTcp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-tcp.json") + AccessLists standardAcls) throws Exception { + aclCustomizer.writeCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyTcpRequest(-1); + } + + @Test + public void updateCurrentAttributesTcp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-tcp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + final Acl data = standardAcls.getAcl().get(0); + aclCustomizer.updateCurrentAttributes(validId, data, data, writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyTcpRequest(4); + } + + + @Test + public void writeCurrentAttributesUdp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-udp.json") + AccessLists standardAcls) throws Exception { + aclCustomizer.writeCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + + verifyUdpRequest(-1); + } + + @Test + public void updateCurrentAttributesUdp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-udp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + final Acl data = standardAcls.getAcl().get(0); + aclCustomizer.updateCurrentAttributes(validId, data, data, writeContext); + + verify(aclApi, times(1)).aclAddReplace(aclAddReplaceRequestCaptor.capture()); + verifyUdpRequest(4); + } + + + @Test + public void deleteCurrentAttributesIcmpIpv4(@InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + aclCustomizer.deleteCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclDel(aclDelRequestCaptor.capture()); + assertEquals(4, aclDelRequestCaptor.getValue().aclIndex); + } + + @Test + public void deleteCurrentAttributesIcmpIpv6( + @InjectTestData(resourcePath = "/acl/standard/standard-acl-icmp-v6.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + aclCustomizer.deleteCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclDel(aclDelRequestCaptor.capture()); + assertEquals(4, aclDelRequestCaptor.getValue().aclIndex); + } + + @Test + public void deleteCurrentAttributesTcp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-tcp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + aclCustomizer.deleteCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclDel(aclDelRequestCaptor.capture()); + assertEquals(4, aclDelRequestCaptor.getValue().aclIndex); + } + + @Test + public void deleteCurrentAttributesUdp(@InjectTestData(resourcePath = "/acl/standard/standard-acl-udp.json") + AccessLists standardAcls) throws Exception { + defineMapping(mappingContext, "standard-acl", 4, "acl-context"); + aclCustomizer.deleteCurrentAttributes(validId, standardAcls.getAcl().get(0), writeContext); + + verify(aclApi, times(1)).aclDel(aclDelRequestCaptor.capture()); + assertEquals(4, aclDelRequestCaptor.getValue().aclIndex); + } + + private void verifyUdpRequest(final int aclIndex) { + final AclAddReplace request = aclAddReplaceRequestCaptor.getValue(); + assertEquals(aclIndex, request.aclIndex); + assertEquals(1, request.count); + assertTrue(Arrays.equals("standard-acl".getBytes(), request.tag)); + + final AclRule udpRule = request.r[0]; + + assertEquals(0, udpRule.isIpv6); + assertEquals(1, udpRule.isPermit); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 2}, udpRule.srcIpAddr)); + assertEquals(32, udpRule.srcIpPrefixLen); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 1}, udpRule.dstIpAddr)); + assertEquals(24, udpRule.dstIpPrefixLen); + + assertEquals(17, udpRule.proto); + assertEquals(1, udpRule.srcportOrIcmptypeFirst); + assertEquals(5487, udpRule.srcportOrIcmptypeLast); + assertEquals(87, udpRule.dstportOrIcmpcodeFirst); + assertEquals(6745, udpRule.dstportOrIcmpcodeLast); + assertEquals(0, udpRule.tcpFlagsMask); + assertEquals(0, udpRule.tcpFlagsValue); + } + + private void verifyTcpRequest(final int aclIndex) { + final AclAddReplace request = aclAddReplaceRequestCaptor.getValue(); + assertEquals(aclIndex, request.aclIndex); + assertEquals(1, request.count); + assertTrue(Arrays.equals("standard-acl".getBytes(), request.tag)); + + final AclRule tcpRule = request.r[0]; + + assertEquals(0, tcpRule.isIpv6); + assertEquals(1, tcpRule.isPermit); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 2}, tcpRule.srcIpAddr)); + assertEquals(32, tcpRule.srcIpPrefixLen); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 1}, tcpRule.dstIpAddr)); + assertEquals(24, tcpRule.dstIpPrefixLen); + + assertEquals(6, tcpRule.proto); + assertEquals(1, tcpRule.srcportOrIcmptypeFirst); + assertEquals(5487, tcpRule.srcportOrIcmptypeLast); + assertEquals(87, tcpRule.dstportOrIcmpcodeFirst); + assertEquals(6745, tcpRule.dstportOrIcmpcodeLast); + assertEquals(1, tcpRule.tcpFlagsMask); + assertEquals(7, tcpRule.tcpFlagsValue); + } + + private void verifyIcmpv6Request(final int aclIndex) { + final AclAddReplace request = aclAddReplaceRequestCaptor.getValue(); + assertEquals(aclIndex, request.aclIndex); + assertEquals(1, request.count); + assertTrue(Arrays.equals("standard-acl".getBytes(), request.tag)); + + final AclRule icmpv6Rule = request.r[0]; + + assertEquals(1, icmpv6Rule.isIpv6); + assertEquals(1, icmpv6Rule.isPermit); + assertTrue( + Arrays.equals(new byte[]{32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 2}, + icmpv6Rule.srcIpAddr)); + assertEquals(48, icmpv6Rule.srcIpPrefixLen); + assertTrue( + Arrays.equals(new byte[]{32, 1, 13, -72, 10, 11, 18, -16, 0, 0, 0, 0, 0, 0, 0, 1}, + icmpv6Rule.dstIpAddr)); + assertEquals(64, icmpv6Rule.dstIpPrefixLen); + + assertEquals(58, icmpv6Rule.proto); + assertEquals(5, icmpv6Rule.srcportOrIcmptypeFirst); + assertEquals(8, icmpv6Rule.srcportOrIcmptypeLast); + assertEquals(1, icmpv6Rule.dstportOrIcmpcodeFirst); + assertEquals(3, icmpv6Rule.dstportOrIcmpcodeLast); + assertEquals(0, icmpv6Rule.tcpFlagsMask); + assertEquals(0, icmpv6Rule.tcpFlagsValue); + } + + private void verifyIcmpIpv4Request(final int aclIndex) { + final AclAddReplace request = aclAddReplaceRequestCaptor.getValue(); + assertEquals(aclIndex, request.aclIndex); + assertEquals(1, request.count); + assertTrue(Arrays.equals("standard-acl".getBytes(), request.tag)); + + final AclRule icmpRule = request.r[0]; + + assertEquals(0, icmpRule.isIpv6); + assertEquals(1, icmpRule.isPermit); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 2}, icmpRule.srcIpAddr)); + assertEquals(32, icmpRule.srcIpPrefixLen); + assertTrue(Arrays.equals(new byte[]{-64, -88, 2, 1}, icmpRule.dstIpAddr)); + assertEquals(24, icmpRule.dstIpPrefixLen); + + assertEquals(1, icmpRule.proto); + assertEquals(5, icmpRule.srcportOrIcmptypeFirst); + assertEquals(8, icmpRule.srcportOrIcmptypeLast); + assertEquals(1, icmpRule.dstportOrIcmpcodeFirst); + assertEquals(3, icmpRule.dstportOrIcmpcodeLast); + assertEquals(0, icmpRule.tcpFlagsMask); + assertEquals(0, icmpRule.tcpFlagsValue); + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/acl/macip/macip-acl.json b/acl/acl-impl/src/test/resources/acl/macip/macip-acl.json new file mode 100644 index 000000000..b944cd79c --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/macip/macip-acl.json @@ -0,0 +1,27 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "macip-acl", + "acl-type": "vpp-acl:vpp-macip-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "macip-rule", + "matches": { + "vpp-macip-ace-nodes": { + "source-ipv4-network": "192.168.2.2/32", + "source-mac-address": "aa:aa:aa:aa:aa:aa", + "source-mac-address-mask": "ff:00:00:00:00:00" + } + }, + "actions": { + "permit": {} + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp-v6.json b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp-v6.json new file mode 100644 index 000000000..08bc61511 --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp-v6.json @@ -0,0 +1,36 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-v6-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv6-network": "2001:0db8:0a0b:12f0:0000:0000:0000:0001/64", + "source-ipv6-network": "2001:0db8:0a0b:12f0:0000:0000:0000:0002/48", + "icmp-v6-nodes": { + "icmp-type-range": { + "first": "5", + "last": "8" + }, + "icmp-code-range": { + "first": "1", + "last": "3" + } + } + } + }, + "actions": { + "permit": {} + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp.json b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp.json new file mode 100644 index 000000000..ce6ff7c2c --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-icmp.json @@ -0,0 +1,36 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/24", + "source-ipv4-network": "192.168.2.2/32", + "icmp-nodes": { + "icmp-type-range": { + "first": "5", + "last": "8" + }, + "icmp-code-range": { + "first": "1", + "last": "3" + } + } + } + }, + "actions": { + "permit": {} + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/acl/standard/standard-acl-tcp.json b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-tcp.json new file mode 100644 index 000000000..f0a1309aa --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-tcp.json @@ -0,0 +1,38 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "tcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/24", + "source-ipv4-network": "192.168.2.2/32", + "tcp-nodes": { + "source-port-range": { + "lower-port": "1", + "upper-port": "5487" + }, + "destination-port-range": { + "lower-port": "87", + "upper-port": "6745" + }, + "tcp-flags-mask": "1", + "tcp-flags-value": "7" + } + } + }, + "actions": { + "permit": {} + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/acl/standard/standard-acl-udp.json b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-udp.json new file mode 100644 index 000000000..77dafeb21 --- /dev/null +++ b/acl/acl-impl/src/test/resources/acl/standard/standard-acl-udp.json @@ -0,0 +1,36 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "udp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/24", + "source-ipv4-network": "192.168.2.2/32", + "udp-nodes": { + "source-port-range": { + "lower-port": "1", + "upper-port": "5487" + }, + "destination-port-range": { + "lower-port": "87", + "upper-port": "6745" + } + } + } + }, + "actions": { + "permit": {} + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/rules/icmp-rule.json b/acl/acl-impl/src/test/resources/rules/icmp-rule.json new file mode 100644 index 000000000..330a448fc --- /dev/null +++ b/acl/acl-impl/src/test/resources/rules/icmp-rule.json @@ -0,0 +1,33 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/32", + "source-ipv4-network": "192.168.2.2/32", + "icmp-nodes": { + "icmp-type-range": { + "first": "5", + "last": "8" + }, + "icmp-code-range": { + "first": "1", + "last": "3" + } + } + } + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/rules/icmp-v6-rule.json b/acl/acl-impl/src/test/resources/rules/icmp-v6-rule.json new file mode 100644 index 000000000..9ea82a19a --- /dev/null +++ b/acl/acl-impl/src/test/resources/rules/icmp-v6-rule.json @@ -0,0 +1,33 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv6-network": "2001:0db8:0a0b:12f0:0000:0000:0000:0001/64", + "source-ipv6-network": "2001:0db8:0a0b:12f0:0000:0000:0000:0002/64", + "icmp-v6-nodes": { + "icmp-type-range": { + "first": "5", + "last": "8" + }, + "icmp-code-range": { + "first": "1", + "last": "3" + } + } + } + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/rules/other-rule.json b/acl/acl-impl/src/test/resources/rules/other-rule.json new file mode 100644 index 000000000..0e60dccf7 --- /dev/null +++ b/acl/acl-impl/src/test/resources/rules/other-rule.json @@ -0,0 +1,26 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/32", + "source-ipv4-network": "192.168.2.2/32", + "other-nodes": { + "protocol": "64" + } + } + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/rules/tcp-rule.json b/acl/acl-impl/src/test/resources/rules/tcp-rule.json new file mode 100644 index 000000000..de3697eef --- /dev/null +++ b/acl/acl-impl/src/test/resources/rules/tcp-rule.json @@ -0,0 +1,35 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/32", + "source-ipv4-network": "192.168.2.2/32", + "tcp-nodes": { + "source-port-range": { + "lower-port": "1", + "upper-port": "5487" + }, + "destination-port-range": { + "lower-port": "87", + "upper-port": "6745" + }, + "tcp-flags-mask": "1", + "tcp-flags-value": "7" + } + } + } + } + ] + } + } + ] + } +}
\ No newline at end of file diff --git a/acl/acl-impl/src/test/resources/rules/udp-rule.json b/acl/acl-impl/src/test/resources/rules/udp-rule.json new file mode 100644 index 000000000..4bc05d4b2 --- /dev/null +++ b/acl/acl-impl/src/test/resources/rules/udp-rule.json @@ -0,0 +1,33 @@ +{ + "access-lists": { + "acl": [ + { + "acl-name": "standard-acl", + "acl-type": "vpp-acl:vpp-acl", + "access-list-entries": { + "ace": [ + { + "rule-name": "imcp-rule", + "matches": { + "vpp-ace-nodes": { + "destination-ipv4-network": "192.168.2.1/32", + "source-ipv4-network": "192.168.2.2/32", + "udp-nodes": { + "source-port-range": { + "lower-port": "1", + "upper-port": "5487" + }, + "destination-port-range": { + "lower-port": "87", + "upper-port": "6745" + } + } + } + } + } + ] + } + } + ] + } +}
\ No newline at end of file |