diff options
author | Marek Gradzki <mgradzki@cisco.com> | 2018-08-23 10:33:47 +0200 |
---|---|---|
committer | Marek Gradzki <mgradzki@cisco.com> | 2018-08-23 12:01:58 +0200 |
commit | bcbe3b71e1e9d12fab817ed001998eae397853cd (patch) | |
tree | a5503fbd12daf6290cb23f75151c35f8b0f80aec | |
parent | f5801c9efa3f79d1d8657849377fef19b6517ca5 (diff) |
HC2VPP-379: move NAT validation code out of customizers
Use Validator interface introduced by HONEYCOMB-431:
https://gerrit.fd.io/r/#/c/14022/
Change-Id: I9e4c8d59f299ed7da4a93bbdc70c81f2bea93606
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
14 files changed, 447 insertions, 115 deletions
diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolCustomizer.java index d7403c004..0e60481d2 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolCustomizer.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolCustomizer.java @@ -54,8 +54,6 @@ final class ExternalIpPoolCustomizer implements ListWriterCustomizer<ExternalIpA public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<ExternalIpAddressPool> id, @Nonnull final ExternalIpAddressPool dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException { - checkArgument(id.firstKeyOf(Instance.class).getId() == 0, - "External IP pools are only assignable for nat instance(vrf-id) with ID 0"); LOG.trace("Adding address range:{}, as: {}", id, dataAfter); // TODO check overlaps ? VPP-478 maybe no necessary, depending on how VPP handles them configureAddressPool(id, dataAfter, true); diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java new file mode 100644 index 000000000..567c8e3b8 --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/ExternalIpPoolValidator.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 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 io.fd.honeycomb.translate.write.DataValidationFailedException; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.ExternalIpAddressPool; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class ExternalIpPoolValidator implements Validator<ExternalIpAddressPool> { + @Override + public void validateWrite(@Nonnull final InstanceIdentifier<ExternalIpAddressPool> id, + @Nonnull final ExternalIpAddressPool dataAfter, @Nonnull final WriteContext writeContext) + throws DataValidationFailedException.CreateValidationFailedException { + if (id.firstKeyOf(Instance.class).getId() != 0) { + throw new DataValidationFailedException.CreateValidationFailedException(id, dataAfter, + new IllegalArgumentException( + "External IP pools are only assignable for nat instance(vrf-id) with ID 0")); + } + } +} diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizer.java index 8d553b872..e51d610e1 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizer.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizer.java @@ -16,9 +16,6 @@ package io.fd.hc2vpp.nat.write; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.base.Preconditions.checkState; - import com.google.common.base.Optional; import io.fd.hc2vpp.common.translate.util.ByteDataTranslator; import io.fd.hc2vpp.common.translate.util.Ipv4Translator; @@ -61,10 +58,6 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, @Nonnull final MappingEntry dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException { - // Only static mapping supported by SNAT for now - checkArgument(dataAfter.getType() == - org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.MappingEntry.Type.Static, - "Only static NAT entries are supported currently. Trying to write: %s entry", dataAfter.getType()); final Long natInstanceId = id.firstKeyOf(Instance.class).getId(); final Long mappingEntryId = id.firstKeyOf(MappingEntry.class).getIndex(); LOG.debug("Writing mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryId); @@ -87,13 +80,10 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, final boolean isAdd) throws WriteFailedException { final IpPrefix internalSrcPrefix = entry.getInternalSrcAddress(); final Ipv4Prefix internalV4SrcPrefix = internalSrcPrefix.getIpv4Prefix(); - final Ipv6Prefix internalV6SrcPrefix = internalSrcPrefix.getIpv6Prefix(); if (internalV4SrcPrefix != null) { final Nat44AddDelStaticMapping request = getNat44Request(id, entry, natInstanceId, isAdd); getReplyForWrite(jvppNat.nat44AddDelStaticMapping(request).toCompletableFuture(), id); } else { - checkState(internalV6SrcPrefix != null, - "internalSrcPrefix.getIpv4Prefix() should not return null if v4 prefix is not given"); final Nat64AddDelStaticBib request = getNat64Request(id, entry, natInstanceId, isAdd); getReplyForWrite(jvppNat.nat64AddDelStaticBib(request).toCompletableFuture(), id); } @@ -148,30 +138,22 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, request.vrfId = natInstanceId.intValue(); final Ipv4Prefix internalAddress = mappingEntry.getInternalSrcAddress().getIpv4Prefix(); - checkArgument(internalAddress != null, "No Ipv4 present in internal-src-address %s", - mappingEntry.getInternalSrcAddress()); - checkArgument(extractPrefix(internalAddress) == 32, - "Only /32 prefix in internal-src-address is supported, but was %s", internalAddress); - request.addrOnly = 1; request.localIpAddress = ipv4AddressPrefixToArray(internalAddress); - request.externalIpAddress = ipv4AddressPrefixToArray(getExternalAddress(mappingEntry)); + request.externalIpAddress = ipv4AddressPrefixToArray(mappingEntry.getExternalSrcAddress().getIpv4Prefix()); request.externalSwIfIndex = -1; // external ip address is ignored if externalSwIfIndex is given request.protocol = -1; final Short protocol = mappingEntry.getTransportProtocol(); if (protocol != null) { - checkArgument(protocol == 1 || protocol == 6 || protocol == 17, - "Unsupported protocol %s only ICMP(1), TCP(6) and UDP(17) are currently supported for Nat44", - protocol); request.protocol = protocol.byteValue(); } - final Short internalPortNumber = getPortNumber(id, mappingEntry.getInternalSrcPort()); - final Short externalPortNumber = getPortNumber(id, mappingEntry.getExternalSrcPort()); + final Integer internalPortNumber = getPortNumber(mappingEntry.getInternalSrcPort()); + final Integer externalPortNumber = getPortNumber(mappingEntry.getExternalSrcPort()); if (internalPortNumber != null && externalPortNumber != null) { request.addrOnly = 0; - request.localPort = internalPortNumber; - request.externalPort = externalPortNumber; + request.localPort = internalPortNumber.shortValue(); + request.externalPort = externalPortNumber.shortValue(); } return request; } @@ -187,48 +169,26 @@ final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, request.vrfId = natInstanceId.intValue(); final Ipv6Prefix internalAddress = mappingEntry.getInternalSrcAddress().getIpv6Prefix(); - checkArgument(internalAddress != null, "No Ipv6 present in internal-src-address %s", - mappingEntry.getInternalSrcAddress()); - checkArgument(extractPrefix(internalAddress) == (byte)128, - "Only /128 prefix in internal-src-address is supported, but was %s", internalAddress); - request.iAddr = ipv6AddressPrefixToArray(internalAddress); - request.oAddr = ipv4AddressPrefixToArray(getExternalAddress(mappingEntry)); + request.oAddr = ipv4AddressPrefixToArray(mappingEntry.getExternalSrcAddress().getIpv4Prefix()); request.proto = -1; final Short protocol = mappingEntry.getTransportProtocol(); if (protocol != null) { - checkArgument(protocol == 1 || protocol == 6 || protocol == 17 || protocol == 58, - "Unsupported protocol %s only ICMP(1), IPv6-ICMP(58), TCP(6) and UDP(17) are currently supported for Nat64", - protocol); request.proto = protocol.byteValue(); } - final Short internalPortNumber = getPortNumber(id, mappingEntry.getInternalSrcPort()); - final Short externalPortNumber = getPortNumber(id, mappingEntry.getExternalSrcPort()); + final Integer internalPortNumber = getPortNumber(mappingEntry.getInternalSrcPort()); + final Integer externalPortNumber = getPortNumber(mappingEntry.getExternalSrcPort()); if (internalPortNumber != null && externalPortNumber != null) { - request.iPort = internalPortNumber; - request.oPort = externalPortNumber; + request.iPort = internalPortNumber.shortValue(); + request.oPort = externalPortNumber.shortValue(); } return request; } - private Ipv4Prefix getExternalAddress(final MappingEntry mappingEntry) { - final Ipv4Prefix externalAddress = mappingEntry.getExternalSrcAddress().getIpv4Prefix(); - checkArgument(externalAddress != null, "No Ipv4 present in external-src-address %s", - mappingEntry.getExternalSrcAddress()); - checkArgument(extractPrefix(externalAddress) == 32, - "Only /32 prefix in external-src-address is supported, but was %s", externalAddress); - return externalAddress; - } - - private Short getPortNumber(final InstanceIdentifier<MappingEntry> id, final PortNumber portNumber) { + private Integer getPortNumber(final PortNumber portNumber) { if (portNumber != null) { - if (portNumber.getStartPortNumber() != null && portNumber.getEndPortNumber() == null) { - return portNumber.getStartPortNumber().getValue().shortValue(); - } else { - throw new IllegalArgumentException( - String.format("Only single port number supported. Submitted: %s for entry: %s", portNumber, id)); - } + return portNumber.getStartPortNumber().getValue(); } return null; } diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java new file mode 100644 index 000000000..d0594039d --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/MappingEntryValidator.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018 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.checkState; + +import com.google.common.annotations.VisibleForTesting; +import io.fd.hc2vpp.common.translate.util.Ipv4Translator; +import io.fd.hc2vpp.common.translate.util.Ipv6Translator; +import io.fd.honeycomb.translate.write.DataValidationFailedException.CreateValidationFailedException; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.PortNumber; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.mapping.table.MappingEntry; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class MappingEntryValidator implements Validator<MappingEntry>, Ipv4Translator, Ipv6Translator { + @Override + public void validateWrite(@Nonnull final InstanceIdentifier<MappingEntry> id, + @Nonnull final MappingEntry mappingEntry, + @Nonnull final WriteContext writeContext) + throws CreateValidationFailedException { + try { + validateMappingEntry(id, mappingEntry); + } catch (RuntimeException e) { + throw new CreateValidationFailedException(id, mappingEntry, e); + } + } + + private void validateMappingEntry(final InstanceIdentifier<MappingEntry> id, final MappingEntry mappingEntry) { + validateMappingEntryType(mappingEntry); + validateInternalSrcAddress(mappingEntry); + validateExternalSrcAddress(mappingEntry); + validateTransportProtocol(mappingEntry); + validatePortNumber(id, mappingEntry.getInternalSrcPort()); + validatePortNumber(id, mappingEntry.getExternalSrcPort()); + } + + @VisibleForTesting + void validateMappingEntryType(final MappingEntry mappingEntry) { + // Only static mapping are currently supported + checkArgument(mappingEntry.getType() + == org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.MappingEntry.Type.Static, + "Only static NAT entries are supported currently. Trying to write: %s entry", mappingEntry.getType()); + } + + @VisibleForTesting + void validateInternalSrcAddress(final MappingEntry mappingEntry) { + final IpPrefix internalSrcPrefix = mappingEntry.getInternalSrcAddress(); + final Ipv4Prefix internalV4SrcPrefix = internalSrcPrefix.getIpv4Prefix(); + final Ipv6Prefix internalV6SrcPrefix = internalSrcPrefix.getIpv6Prefix(); + if (internalV4SrcPrefix != null) { + checkArgument(extractPrefix(internalV4SrcPrefix) == 32, + "Only /32 prefix in internal-src-address is supported, but was %s", internalV4SrcPrefix); + } else { + checkState(internalV6SrcPrefix != null, + "internalSrcPrefix.getIpv6Prefix() should not return null if Ipv4 prefix is not given"); + checkArgument(extractPrefix(internalV6SrcPrefix) == (byte) 128, + "Only /128 prefix in internal-src-address is supported, but was %s", internalV6SrcPrefix); + } + } + + @VisibleForTesting + void validateExternalSrcAddress(final MappingEntry mappingEntry) { + final IpPrefix externalSrcAddress = mappingEntry.getExternalSrcAddress(); + checkArgument(externalSrcAddress != null, "The external-src-address leaf is missing"); + final Ipv4Prefix ipv4Prefix = externalSrcAddress.getIpv4Prefix(); + checkArgument(ipv4Prefix != null, "No Ipv4 present in external-src-address %s", externalSrcAddress); + checkArgument(extractPrefix(ipv4Prefix) == 32, + "Only /32 prefix in external-src-address is supported, but was %s", ipv4Prefix); + } + + @VisibleForTesting + static void validateTransportProtocol(final MappingEntry mappingEntry) { + final Short protocol = mappingEntry.getTransportProtocol(); + if (protocol == null) { + return; + } + checkArgument(protocol == 1 || protocol == 6 || protocol == 17 || protocol == 58, + "Unsupported protocol %s only ICMP(1), IPv6-ICMP(58), TCP(6) and UDP(17) are currently supported", + protocol); + } + + @VisibleForTesting + static void validatePortNumber(final InstanceIdentifier<MappingEntry> id, final PortNumber portNumber) { + if (portNumber == null) { + return; + } + checkArgument(portNumber.getStartPortNumber() != null && portNumber.getEndPortNumber() == null, + "Only single port number supported. Submitted: %s for entry: %s", portNumber, id); + } +} 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 index 8f1869f15..13a5845e3 100644 --- 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 @@ -56,12 +56,6 @@ final class Nat64PrefixesCustomizer @Nonnull final WriteContext writeContext) throws WriteFailedException { final int natInstanceId = id.firstKeyOf(Instance.class).getId().intValue(); LOG.debug("Configuring nat64 prefix: {} for nat-instance(vrf): {}", dataAfter, natInstanceId); - - // VPP does not support configuring different nat64-prefixes depending on ipv4 destination prefix: - final List<DestinationIpv4Prefix> 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); } @@ -83,10 +77,7 @@ final class Nat64PrefixesCustomizer 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); diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java new file mode 100644 index 000000000..3b6dc9553 --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/Nat64PrefixesValidator.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 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 io.fd.honeycomb.translate.write.DataValidationFailedException; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; +import java.util.List; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.Nat64Prefixes; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.nat64.prefixes.DestinationIpv4Prefix; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class Nat64PrefixesValidator implements Validator<Nat64Prefixes> { + @Override + public void validateWrite(@Nonnull final InstanceIdentifier<Nat64Prefixes> id, + @Nonnull final Nat64Prefixes dataAfter, + @Nonnull final WriteContext writeContext) + throws DataValidationFailedException.CreateValidationFailedException { + // VPP does not support configuring different nat64-prefixes depending on ipv4 destination prefix: + final List<DestinationIpv4Prefix> destinationIpv4PrefixList = dataAfter.getDestinationIpv4Prefix(); + checkArgument(destinationIpv4PrefixList == null || destinationIpv4PrefixList.isEmpty(), + "destination-ipv4-prefix is not supported by VPP"); + } +} diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstaceCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstaceCustomizer.java index 90caa7c76..945a5fa58 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstaceCustomizer.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstaceCustomizer.java @@ -42,12 +42,5 @@ final class NatInstaceCustomizer implements ListWriterCustomizer<Instance, Insta @Nonnull final Instance dataBefore, @Nonnull final WriteContext writeContext) throws WriteFailedException { LOG.trace("Deleting NAT instance: {}", id); - - // For consistency with reader, forbid removing default NAT instance: - final Long vrfId = id.firstKeyOf(Instance.class).getId(); - if (vrfId == 0) { - throw new WriteFailedException.DeleteFailedException(id, - new UnsupportedOperationException("Removing default NAT instance (vrf=0) is not supported.")); - } } } diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java new file mode 100644 index 000000000..1f3975513 --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/NatInstanceValidator.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 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 io.fd.honeycomb.translate.write.DataValidationFailedException; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class NatInstanceValidator implements Validator<Instance> { + @Override + public void validateDelete(@Nonnull final InstanceIdentifier<Instance> id, @Nonnull final Instance dataBefore, + @Nonnull final WriteContext writeContext) + throws DataValidationFailedException.DeleteValidationFailedException { + // For consistency with reader, forbid removing default NAT instance: + final Long vrfId = id.firstKeyOf(Instance.class).getId(); + if (vrfId == 0) { + throw new DataValidationFailedException.DeleteValidationFailedException(id, + new UnsupportedOperationException("Removing default NAT instance (vrf=0) is not supported.")); + } + } +} 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 267148907..5b0478ff9 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 @@ -58,14 +58,16 @@ public final class NatWriterFactory implements WriterFactory { public void init(@Nonnull final ModifiableWriterRegistryBuilder registry) { // +-- nat // +-- instances/instance - registry.add(new GenericListWriter<>(NAT_INSTANCE_ID, new NatInstaceCustomizer())); + registry.add(new GenericListWriter<>(NAT_INSTANCE_ID, new NatInstaceCustomizer(), new NatInstanceValidator())); // +-- mapping-table/mapping-entry registry.subtreeAdd(Sets.newHashSet(InstanceIdentifier.create(MappingEntry.class).child(ExternalSrcPort.class), - InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)), - new GenericListWriter<>(MAPPING_ENTRY_ID, new MappingEntryCustomizer(jvppNat, mappingEntryContext))); + InstanceIdentifier.create(MappingEntry.class).child(InternalSrcPort.class)), + new GenericListWriter<>(MAPPING_ENTRY_ID, + new MappingEntryCustomizer(jvppNat, mappingEntryContext), + new MappingEntryValidator())); // +-- policy - registry.add(new GenericListWriter<>(POLICY_ID, new PolicyCustomizer())); + registry.add(new GenericListWriter<>(POLICY_ID, new PolicyCustomizer(), new PolicyValidator())); // +-- external-ip-address-pool registry.subtreeAddBefore( @@ -73,11 +75,16 @@ public final class NatWriterFactory implements WriterFactory { // requires to already have an IP range predefined ... in some cases Sets.newHashSet(InstanceIdentifier.create(ExternalIpAddressPool.class) .augmentation(ExternalIpAddressPoolAugmentation.class)), - new GenericListWriter<>(ADDRESS_POOL_ID, new ExternalIpPoolCustomizer(jvppNat)), MAPPING_ENTRY_ID); + new GenericListWriter<>(ADDRESS_POOL_ID, + new ExternalIpPoolCustomizer(jvppNat), + new ExternalIpPoolValidator()), + MAPPING_ENTRY_ID); // +-- nat64-prefixes registry.subtreeAdd( - Sets.newHashSet(InstanceIdentifier.create(Nat64Prefixes.class).child(DestinationIpv4Prefix.class)), - new GenericListWriter<>(NAT64_PREFIXES_ID, new Nat64PrefixesCustomizer(jvppNat))); + Sets.newHashSet(InstanceIdentifier.create(Nat64Prefixes.class).child(DestinationIpv4Prefix.class)), + new GenericListWriter<>(NAT64_PREFIXES_ID, + new Nat64PrefixesCustomizer(jvppNat), + new Nat64PrefixesValidator())); } } diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyCustomizer.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyCustomizer.java index 09e404a53..782fa3bf7 100644 --- a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyCustomizer.java +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyCustomizer.java @@ -38,17 +38,6 @@ final class PolicyCustomizer implements ListWriterCustomizer<Policy, PolicyKey> @Nonnull final Policy dataAfter, @Nonnull final WriteContext writeContext) throws WriteFailedException { LOG.trace("Writing NAT policy: {}", id); - - // HC supports only single NAT policy per NAT instance (VRF) - // To ensure that (and for simplicity), we require policy id = 0. - final Long policyId = id.firstKeyOf(Policy.class).getId(); - checkArgument(policyId == 0, - "Only single policy per NAT instance (VRF) is supported (expected id=0, but %s given)", policyId); - - if (dataAfter.getNat64Prefixes() != null) { - final int prefixCount = dataAfter.getNat64Prefixes().size(); - checkArgument(prefixCount <= 1, "Only single nat64-prefix is supported, but %s given", prefixCount); - } } @Override diff --git a/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java new file mode 100644 index 000000000..8b13a0851 --- /dev/null +++ b/nat/nat2vpp/src/main/java/io/fd/hc2vpp/nat/write/PolicyValidator.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018 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 io.fd.honeycomb.translate.write.DataValidationFailedException; +import io.fd.honeycomb.translate.write.Validator; +import io.fd.honeycomb.translate.write.WriteContext; +import javax.annotation.Nonnull; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.Policy; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +final class PolicyValidator implements Validator<Policy> { + @Override + public void validateWrite(@Nonnull final InstanceIdentifier<Policy> id, @Nonnull final Policy dataAfter, + @Nonnull final WriteContext writeContext) + throws DataValidationFailedException.CreateValidationFailedException { + try { + validatePolicy(id, dataAfter); + } catch (RuntimeException e) { + throw new DataValidationFailedException.CreateValidationFailedException(id, dataAfter, e); + } + } + + private void validatePolicy(final InstanceIdentifier<Policy> id, final Policy policy) { + // HC supports only single NAT policy per NAT instance (VRF) + // To ensure that (and for simplicity), we require policy id = 0. + final Long policyId = id.firstKeyOf(Policy.class).getId(); + checkArgument(policyId == 0, + "Only single policy per NAT instance (VRF) is supported (expected id=0, but %s given)", policyId); + + if (policy.getNat64Prefixes() != null) { + final int prefixCount = policy.getNat64Prefixes().size(); + checkArgument(prefixCount <= 1, "Only single nat64-prefix is supported, but %s given", prefixCount); + } + } +} diff --git a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizerTest.java b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizerTest.java index 60cc643e9..ede39e4b3 100644 --- a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizerTest.java +++ b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryCustomizerTest.java @@ -47,11 +47,11 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements private static final long NAT_INSTANCE_ID = 1; private static final long MAPPING_ID = 22; - private static final InstanceIdentifier<MappingEntry> IID = NAT_INSTANCES_ID + static final InstanceIdentifier<MappingEntry> IID = NAT_INSTANCES_ID .child(Instance.class, new InstanceKey(NAT_INSTANCE_ID)) .child(MappingTable.class).child(MappingEntry.class, new MappingEntryKey(MAPPING_ID)); - private static final String MAPPING_TABLE_PATH = "/ietf-nat:nat/ietf-nat:instances/" + static final String MAPPING_TABLE_PATH = "/ietf-nat:nat/ietf-nat:instances/" + "ietf-nat:instance[ietf-nat:id='" + NAT_INSTANCE_ID + "']/ietf-nat:mapping-table"; @Mock @@ -87,13 +87,6 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements verify(jvppNat).nat64AddDelStaticBib(expectedRequest); } - @Test(expected = IllegalArgumentException.class) - public void testWriteNat44UnsupportedProtocol( - @InjectTestData(resourcePath = "/nat44/static-mapping-unsupported-proto.json", id = MAPPING_TABLE_PATH) MappingTable data) - throws WriteFailedException { - customizer.writeCurrentAttributes(IID, extractMappingEntry(data), writeContext); - } - @Test(expected = UnsupportedOperationException.class) public void testUpdateNat64( @InjectTestData(resourcePath = "/nat64/static-mapping.json", id = MAPPING_TABLE_PATH) MappingTable before, @@ -123,7 +116,7 @@ public class MappingEntryCustomizerTest extends WriterCustomizerTest implements verify(jvppNat).nat64AddDelStaticBib(getExpectedNat64Request()); } - private static MappingEntry extractMappingEntry(MappingTable data) { + static MappingEntry extractMappingEntry(MappingTable data) { // assumes single nat instance and single mapping entry return data.getMappingEntry().get(0); } diff --git a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java new file mode 100644 index 000000000..1cb98cc0e --- /dev/null +++ b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/MappingEntryValidatorTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018 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 io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.IID; +import static io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.MAPPING_TABLE_PATH; +import static io.fd.hc2vpp.nat.write.MappingEntryCustomizerTest.extractMappingEntry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.mockito.MockitoAnnotations.initMocks; + +import io.fd.hc2vpp.nat.NatTestSchemaContext; +import io.fd.honeycomb.test.tools.HoneycombTestRunner; +import io.fd.honeycomb.test.tools.annotations.InjectTestData; +import io.fd.honeycomb.translate.write.DataValidationFailedException; +import io.fd.honeycomb.translate.write.WriteContext; +import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.vpp.jvpp.nat.dto.Nat44AddDelStaticMapping; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; +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.nat.rev180628.MappingEntry.Type; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.mapping.entry.InternalSrcPort; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.mapping.entry.InternalSrcPortBuilder; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.MappingTable; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.mapping.table.MappingEntry; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +@RunWith(HoneycombTestRunner.class) +public class MappingEntryValidatorTest implements NatTestSchemaContext { + @Mock + private WriteContext writeContext; + private MappingEntryValidator validator; + + @Before + public void setUp() { + initMocks(this); + validator = new MappingEntryValidator(); + } + + @Test + public void testWriteNat44( + @InjectTestData(resourcePath = "/nat44/static-mapping.json", id = MAPPING_TABLE_PATH) MappingTable data) + throws WriteFailedException, DataValidationFailedException.CreateValidationFailedException { + validator.validateWrite(IID, extractMappingEntry(data), writeContext); + } + + @Test(expected = DataValidationFailedException.CreateValidationFailedException.class) + public void testWriteNat44UnsupportedProtocol( + @InjectTestData(resourcePath = "/nat44/static-mapping-unsupported-proto.json", id = MAPPING_TABLE_PATH) MappingTable data) + throws WriteFailedException, DataValidationFailedException.CreateValidationFailedException { + validator.validateWrite(IID, extractMappingEntry(data), writeContext); + } + + @Test(expected = IllegalArgumentException.class) + public void testUnsupportedMappingEntryType() { + final MappingEntry mappingEntry = mock(MappingEntry.class); + when(mappingEntry.getType()).thenReturn(Type.DynamicExplicit); + validator.validateMappingEntryType(mappingEntry); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidInternalIpv6SrcAddressPrefix() { + final MappingEntry mappingEntry = mock(MappingEntry.class); + final IpPrefix address = new IpPrefix(new Ipv6Prefix("1::1/127")); + when(mappingEntry.getInternalSrcAddress()).thenReturn(address); + validator.validateInternalSrcAddress(mappingEntry); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidInternalIpv4SrcAddressPrefix() { + final MappingEntry mappingEntry = mock(MappingEntry.class); + final IpPrefix address = new IpPrefix(new Ipv4Prefix("1.2.3.4/16")); + when(mappingEntry.getInternalSrcAddress()).thenReturn(address); + validator.validateInternalSrcAddress(mappingEntry); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidExternalSrcAddress() { + final MappingEntry mappingEntry = mock(MappingEntry.class); + final IpPrefix address = new IpPrefix(new Ipv4Prefix("1.2.3.4/16")); + when(mappingEntry.getExternalSrcAddress()).thenReturn(address); + validator.validateExternalSrcAddress(mappingEntry); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidPortNumber() { + InternalSrcPort port = new InternalSrcPortBuilder() + .setStartPortNumber(new PortNumber(10)) + .setEndPortNumber(new PortNumber(20)) + .build(); + final InstanceIdentifier<MappingEntry> id = InstanceIdentifier.create(MappingEntry.class); + MappingEntryValidator.validatePortNumber(id, port); + } +}
\ No newline at end of file diff --git a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyCustomizerTest.java b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyValidatorTest.java index faa4ced4d..7cee98d4a 100644 --- a/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyCustomizerTest.java +++ b/nat/nat2vpp/src/test/java/io/fd/hc2vpp/nat/write/PolicyValidatorTest.java @@ -18,12 +18,15 @@ package io.fd.hc2vpp.nat.write; import static io.fd.hc2vpp.nat.NatIds.NAT_INSTANCES_ID; import static org.mockito.Mockito.mock; +import static org.mockito.MockitoAnnotations.initMocks; -import io.fd.hc2vpp.common.test.write.WriterCustomizerTest; -import io.fd.honeycomb.translate.write.WriteFailedException; +import io.fd.honeycomb.translate.write.DataValidationFailedException.CreateValidationFailedException; +import io.fd.honeycomb.translate.write.WriteContext; import java.util.Arrays; import java.util.Collections; +import org.junit.Before; import org.junit.Test; +import org.mockito.Mock; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Prefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.Instance; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.InstanceKey; @@ -34,7 +37,7 @@ import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev1806 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev180628.nat.instances.instance.policy.Nat64PrefixesBuilder; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; -public class PolicyCustomizerTest extends WriterCustomizerTest { +public class PolicyValidatorTest { private static final long VRF_ID = 123; private static final InstanceIdentifier<Instance> NAT_INSTANCE_ID = @@ -48,32 +51,35 @@ public class PolicyCustomizerTest extends WriterCustomizerTest { private static final Nat64Prefixes P2 = new Nat64PrefixesBuilder().setNat64Prefix(new Ipv6Prefix("2001:db8::2/32")).build(); - private PolicyCustomizer customizer; + @Mock + private WriteContext writeContext; + private PolicyValidator validator; - @Override - protected void setUpTest() throws Exception { - customizer = new PolicyCustomizer(); + @Before + public void setUp() throws Exception { + initMocks(this); + validator = new PolicyValidator(); } - @Test(expected = IllegalArgumentException.class) - public void testInvalidPolicyId() throws WriteFailedException { - customizer.writeCurrentAttributes(INVALID_POLICY_ID, mock(Policy.class), writeContext); + @Test(expected = CreateValidationFailedException.class) + public void testInvalidPolicyId() throws CreateValidationFailedException { + validator.validateWrite(INVALID_POLICY_ID, mock(Policy.class), writeContext); } @Test - public void testNoNat64Prefixes() throws WriteFailedException { - customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, mock(Policy.class), writeContext); + public void testNoNat64Prefixes() throws CreateValidationFailedException { + validator.validateWrite(DEFAULT_POLICY_ID, mock(Policy.class), writeContext); } @Test - public void testSingleNat64Prefix() throws WriteFailedException { + public void testSingleNat64Prefix() throws CreateValidationFailedException { final Policy policy = new PolicyBuilder().setNat64Prefixes(Collections.singletonList(P1)).build(); - customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, policy, writeContext); + validator.validateWrite(DEFAULT_POLICY_ID, policy, writeContext); } - @Test(expected = IllegalArgumentException.class) - public void testTwoNat64Prefixes() throws WriteFailedException { + @Test(expected = CreateValidationFailedException.class) + public void testTwoNat64Prefixes() throws CreateValidationFailedException { final Policy policy = new PolicyBuilder().setNat64Prefixes(Arrays.asList(P1, P2)).build(); - customizer.writeCurrentAttributes(DEFAULT_POLICY_ID, policy, writeContext); + validator.validateWrite(DEFAULT_POLICY_ID, policy, writeContext); } }
\ No newline at end of file |