From 29456ce94c09768405c31f8fb315425b3c98638f Mon Sep 17 00:00:00 2001 From: Marek Gradzki Date: Tue, 22 Aug 2017 14:33:22 +0200 Subject: HC2VPP-105: nat64 prefix configuration Change-Id: I205fb426ab9c0e47ef40b81c2f6dcd397524f1eb Signed-off-by: Marek Gradzki Signed-off-by: Jan Srnicek --- .../hc2vpp/nat/write/Nat64PrefixesCustomizer.java | 103 +++++++++++++++++++++ .../io/fd/hc2vpp/nat/write/NatWriterFactory.java | 10 ++ .../nat/write/Nat64PrefixesCustomizerTest.java | 99 ++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizer.java create mode 100644 nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizerTest.java diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizer.java new file mode 100644 index 000000000..b62733e75 --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizer.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2017 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.nat.write; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.hc2vpp.common.translate.util.Ipv6Translator; +import io.fd.hc2vpp.common.translate.util.JvppReplyConsumer; +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.nat.dto.Nat64AddDelPrefix; +import io.fd.vpp.jvpp.nat.future.FutureJVppNatFacade; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64Prefixes; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64PrefixesKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.nat64.prefixes.DestinationIpv4Prefix; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class Nat64PrefixesCustomizer + implements ListWriterCustomizer, ByteDataTranslator, Ipv6Translator, + JvppReplyConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(Nat64PrefixesCustomizer.class); + + private final FutureJVppNatFacade jvppNat; + + Nat64PrefixesCustomizer(final FutureJVppNatFacade jvppNat) { + this.jvppNat = checkNotNull(jvppNat, "jvppNat should not be null"); + } + + @Override + public void writeCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final Nat64Prefixes dataAfter, + @Nonnull final WriteContext writeContext) throws WriteFailedException { + final int natInstanceId = id.firstKeyOf(NatInstance.class).getId().intValue(); + LOG.debug("Configuring nat64 prefix: {} for nat-instance(vrf): {}", dataAfter, natInstanceId); + + // VPP supports only single nat64-prefix per VRF/nat-instance (we map nat-instances to VRFs) + // To ensure that (and for simplicity), we require nat64-prefix-id = 0. + final Long nat64PrefixId = id.firstKeyOf(Nat64Prefixes.class).getNat64PrefixId(); + checkArgument(nat64PrefixId == 0, "Only single nat64 prefix is supported (expected id=0, but %s given)", + nat64PrefixId); + + // VPP does not support configuring different nat64-prefixes depending on ipv4 destination prefix: + final List destinationIpv4PrefixList = dataAfter.getDestinationIpv4Prefix(); + checkArgument(destinationIpv4PrefixList == null || destinationIpv4PrefixList.isEmpty(), + "destination-ipv4-prefix is not supported by VPP"); + + addDelPrefix(id, dataAfter, natInstanceId, true); + LOG.debug("Nat64 prefix written successfully: {} for nat-instance(vrf): {}", dataAfter, natInstanceId); + } + + @Override + public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier id, + @Nonnull final Nat64Prefixes dataBefore, + @Nonnull final WriteContext writeContext) + throws WriteFailedException { + final int natInstanceId = id.firstKeyOf(NatInstance.class).getId().intValue(); + LOG.debug("Removing nat64 prefix configuration: {} for nat-instance(vrf): {}", dataBefore, natInstanceId); + // No need for validation here (it was done on write) + addDelPrefix(id, dataBefore, natInstanceId, false); + LOG.debug("Nat64 prefix removed successfully: {} for nat-instance(vrf): {}", dataBefore, natInstanceId); + + } + + private void addDelPrefix(@Nonnull final InstanceIdentifier id, @Nonnull final Nat64Prefixes data, + final int vrfId, final boolean isAdd) + throws WriteFailedException { + + // The nat64-prefix is optional in ietf-nat, but we require it + final Ipv6Prefix nat64Prefix = data.getNat64Prefix(); + checkArgument(nat64Prefix != null, "Missing nat64-prefix leaf value."); + + final Nat64AddDelPrefix request = new Nat64AddDelPrefix(); + request.prefix = ipv6AddressPrefixToArray(nat64Prefix); + request.prefixLen = extractPrefix(nat64Prefix); + request.isAdd = booleanToByte(isAdd); + request.vrfId = vrfId; + getReplyForWrite(jvppNat.nat64AddDelPrefix(request).toCompletableFuture(), id); + } +} diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatWriterFactory.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatWriterFactory.java index e315da637..764da68b8 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatWriterFactory.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatWriterFactory.java @@ -32,6 +32,8 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev1509 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.MappingTable; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.ExternalIpAddressPool; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64Prefixes; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.nat64.prefixes.DestinationIpv4Prefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.vpp.nat.rev170804.ExternalIpAddressPoolConfigAugmentation; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; @@ -45,6 +47,8 @@ public final class NatWriterFactory implements WriterFactory { NAT_CFG_ID.child(NatInstances.class).child(NatInstance.class); private static final InstanceIdentifier MAP_ENTRY_ID = NAT_INSTANCE_ID.child(MappingTable.class).child(MappingEntry.class); + private static final InstanceIdentifier NAT64_PREFIXES = + NAT_INSTANCE_ID.child(Nat64Prefixes.class); private final FutureJVppNatFacade jvppNat; private final MappingEntryContext mappingEntryContext; @@ -73,5 +77,11 @@ public final class NatWriterFactory implements WriterFactory { new GenericListWriter<>(NAT_INSTANCE_ID.child(ExternalIpAddressPool.class), new ExternalIpPoolCustomizer(jvppNat)), MAP_ENTRY_ID); + + // nat64-prefixes + registry.subtreeAdd( + Sets.newHashSet(InstanceIdentifier.create(Nat64Prefixes.class).child(DestinationIpv4Prefix.class)), + new GenericListWriter<>(NAT_INSTANCE_ID.child(Nat64Prefixes.class), + new Nat64PrefixesCustomizer(jvppNat))); } } diff --git a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizerTest.java b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizerTest.java new file mode 100644 index 000000000..63a3b4673 --- /dev/null +++ b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/Nat64PrefixesCustomizerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2017 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.nat.write; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import io.fd.hc2vpp.common.test.write.WriterCustomizerTest; +import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; +import io.fd.vpp.jvpp.nat.dto.Nat64AddDelPrefix; +import io.fd.vpp.jvpp.nat.dto.Nat64AddDelPrefixReply; +import io.fd.vpp.jvpp.nat.future.FutureJVppNatFacade; +import java.util.Collections; +import org.junit.Test; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.NatConfig; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.NatInstances; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstanceKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64Prefixes; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64PrefixesBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.Nat64PrefixesKey; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.parameters.nat64.prefixes.DestinationIpv4Prefix; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +public class Nat64PrefixesCustomizerTest extends WriterCustomizerTest implements ByteDataTranslator { + + private static final long VRF_ID = 123; + + private static final InstanceIdentifier NAT_INSTANCE_ID = + InstanceIdentifier.create(NatConfig.class).child(NatInstances.class).child(NatInstance.class, new NatInstanceKey(VRF_ID)); + + private static final Nat64Prefixes VALID_DATA = new Nat64PrefixesBuilder().setNat64Prefix(new Ipv6Prefix("2001:db8::/32")).build(); + + @Mock + private FutureJVppNatFacade jvppNat; + + private Nat64PrefixesCustomizer customizer; + + @Override + protected void setUpTest() throws Exception { + customizer = new Nat64PrefixesCustomizer(jvppNat); + when(jvppNat.nat64AddDelPrefix(any())).thenReturn(future(new Nat64AddDelPrefixReply())); + } + + @Test(expected = IllegalArgumentException.class) + public void testWriteNonZeroPrefixIdFails() throws Exception { + customizer.writeCurrentAttributes(getID(1), mock(Nat64Prefixes.class), writeContext); + } + + @Test(expected = IllegalArgumentException.class) + public void testWriteDestinationPrefixFails() throws Exception { + final Nat64Prefixes data = mock(Nat64Prefixes.class); + when(data.getDestinationIpv4Prefix()).thenReturn(Collections.singletonList(mock(DestinationIpv4Prefix.class))); + customizer.writeCurrentAttributes(getID(1), data, writeContext); + } + + @Test + public void testWrite() throws Exception { + customizer.writeCurrentAttributes(getID(0), VALID_DATA, writeContext); + verify(jvppNat).nat64AddDelPrefix(expectedRequest(true)); + } + + @Test + public void testDelete() throws Exception { + customizer.deleteCurrentAttributes(getID(0), VALID_DATA, writeContext); + verify(jvppNat).nat64AddDelPrefix(expectedRequest(false)); + } + + private static InstanceIdentifier getID(final long prefixId) { + return NAT_INSTANCE_ID.child(Nat64Prefixes.class, new Nat64PrefixesKey(prefixId)); + } + + private Nat64AddDelPrefix expectedRequest(final boolean isAdd) { + final Nat64AddDelPrefix request = new Nat64AddDelPrefix(); + request.isAdd = booleanToByte(isAdd); + request.vrfId = (int) VRF_ID; + request.prefix = new byte[]{0x20, 0x01, 0x0d, (byte) 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + request.prefixLen = 32; + return request; + } +} \ No newline at end of file -- cgit 1.2.3-korg