2 * Copyright (c) 2016 Cisco and/or its affiliates.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package io.fd.honeycomb.nat.write;
19 import static com.google.common.base.Preconditions.checkArgument;
21 import com.google.common.base.Optional;
22 import io.fd.honeycomb.nat.util.MappingEntryContext;
23 import io.fd.honeycomb.translate.spi.write.ListWriterCustomizer;
24 import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
25 import io.fd.honeycomb.translate.vpp.util.JvppReplyConsumer;
26 import io.fd.honeycomb.translate.write.WriteContext;
27 import io.fd.honeycomb.translate.write.WriteFailedException;
28 import io.fd.vpp.jvpp.snat.dto.SnatAddStaticMapping;
29 import io.fd.vpp.jvpp.snat.future.FutureJVppSnatFacade;
30 import javax.annotation.Nonnull;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.PortNumber;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.NatInstance;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntryKey;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.port.number.PortType;
36 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.port.number.port.type.SinglePortNumber;
37 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
41 final class MappingEntryCustomizer implements ListWriterCustomizer<MappingEntry, MappingEntryKey>,
42 JvppReplyConsumer, Ipv4Translator {
44 private static final Logger LOG = LoggerFactory.getLogger(MappingEntryCustomizer.class);
46 private final FutureJVppSnatFacade jvppSnat;
47 private final MappingEntryContext mappingEntryContext;
49 MappingEntryCustomizer(final FutureJVppSnatFacade jvppSnat, final MappingEntryContext mappingEntryContext) {
50 this.jvppSnat = jvppSnat;
51 this.mappingEntryContext = mappingEntryContext;
55 public void writeCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
56 @Nonnull final MappingEntry dataAfter,
57 @Nonnull final WriteContext writeContext)
58 throws WriteFailedException {
59 // Only static mapping supported by SNAT for now
60 checkArgument(dataAfter.getType() ==
61 org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.MappingEntry.Type.Static,
62 "Only static NAT entries are supported currently. Trying to write: %s entry", dataAfter.getType());
63 final Long natInstanceId = id.firstKeyOf(NatInstance.class).getId();
64 final Long mappingEntryId = id.firstKeyOf(MappingEntry.class).getIndex();
65 LOG.debug("Writing mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryId);
67 final SnatAddStaticMapping request = getRequest(id, dataAfter, natInstanceId, true);
68 getReplyForWrite(jvppSnat.snatAddStaticMapping(request).toCompletableFuture(), id);
70 // Store context mapping only if not already present under the same exact mapping
71 synchronized (mappingEntryContext) {
72 if (shouldStoreContextMapping(natInstanceId, mappingEntryId, dataAfter, writeContext)) {
74 .addEntry(natInstanceId, mappingEntryId, dataAfter, writeContext.getMappingContext());
77 LOG.trace("Mapping entry: {} for nat-instance(vrf): {} written successfully", request.vrfId, id);
81 * Check whether entry is already stored in context under the same index.
83 * @return true if it's not yet stored under same index, false otherwise.
85 private boolean shouldStoreContextMapping(final long natInstanceId, final long mappingEntryId,
86 final MappingEntry dataAfter,
87 final WriteContext writeCtx) {
88 if (!mappingEntryContext.containsEntry(natInstanceId, dataAfter, writeCtx.getMappingContext())) {
92 final Optional<Long> storedIndex =
93 mappingEntryContext.getStoredIndex(natInstanceId, dataAfter, writeCtx.getMappingContext());
94 if (!storedIndex.isPresent()) {
98 if (storedIndex.get() != mappingEntryId) {
106 public void updateCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
107 @Nonnull final MappingEntry dataBefore,
108 @Nonnull final MappingEntry dataAfter,
109 @Nonnull final WriteContext writeContext) throws WriteFailedException {
110 throw new WriteFailedException.UpdateFailedException(id, dataBefore, dataAfter,
111 new UnsupportedOperationException("Mapping entry update not supported"));
115 public void deleteCurrentAttributes(@Nonnull final InstanceIdentifier<MappingEntry> id,
116 @Nonnull final MappingEntry dataBefore,
117 @Nonnull final WriteContext writeContext) throws WriteFailedException {
118 final long natInstanceId = id.firstKeyOf(NatInstance.class).getId();
119 final MappingEntryKey mappingEntryKey = id.firstKeyOf(MappingEntry.class);
120 LOG.debug("Deleting mapping entry: {} for nat-instance(vrf): {}", natInstanceId, mappingEntryKey);
122 final SnatAddStaticMapping request = getRequest(id, dataBefore, natInstanceId, false);
123 getReplyForWrite(jvppSnat.snatAddStaticMapping(request).toCompletableFuture(), id);
124 mappingEntryContext.removeEntry(natInstanceId, dataBefore, writeContext.getMappingContext());
125 LOG.trace("Mapping entry: {} for nat-instance(vrf): {} deleted successfully", request.vrfId, id);
128 private SnatAddStaticMapping getRequest(final InstanceIdentifier<MappingEntry> id,
129 final MappingEntry dataAfter,
130 final Long natInstanceId,
132 throws WriteFailedException.CreateFailedException {
133 final SnatAddStaticMapping request = new SnatAddStaticMapping();
134 request.isAdd = isAdd
138 // VPP uses int, model long
139 request.vrfId = natInstanceId.intValue();
141 // Snat supports only ipv4 now
142 if (dataAfter.getInternalSrcAddress().getIpv4Address() == null) {
143 throw new WriteFailedException.CreateFailedException(id, dataAfter,
144 new UnsupportedOperationException(
145 String.format("No Ipv4 present for in address %s. Ipv6 not supported",
146 dataAfter.getInternalSrcAddress())));
149 request.addrOnly = 1;
150 request.localIpAddress =
151 ipv4AddressNoZoneToArray(dataAfter.getInternalSrcAddress().getIpv4Address().getValue());
152 request.externalIpAddress = ipv4AddressNoZoneToArray(dataAfter.getExternalSrcAddress().getValue());
154 Optional<Short> internalPortNumber = getPortNumber(id, dataAfter,
155 (entry) -> Optional.fromNullable(entry.getInternalSrcPort()).transform(PortNumber::getPortType));
156 Optional<Short> externalPortNumber = getPortNumber(id, dataAfter,
157 (entry) -> Optional.fromNullable(entry.getExternalSrcPort()).transform(PortNumber::getPortType));
158 if (internalPortNumber.isPresent() && externalPortNumber.isPresent()) {
159 request.addrOnly = 0;
160 request.localPort = internalPortNumber.get();
161 request.externalPort = externalPortNumber.get();
166 private Optional<Short> getPortNumber(final InstanceIdentifier<MappingEntry> id, final MappingEntry dataAfter,
167 final PortGetter portGetter) {
168 return portGetter.getPortType(dataAfter).transform(port -> {
169 if (port instanceof SinglePortNumber) {
170 return ((SinglePortNumber) port).getSinglePortNumber().getValue().shortValue();
172 throw new IllegalArgumentException(
173 String.format("Only single port number supported. Submitted: %s for entry: %s",
174 dataAfter.getInternalSrcPort(), id));
179 interface PortGetter {
180 Optional<PortType> getPortType(MappingEntry entry);