HONEYCOMB-58 - Routing Api
[honeycomb.git] / nat / nat2vpp / src / main / java / io / fd / honeycomb / nat / util / MappingEntryContext.java
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  *
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:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package io.fd.honeycomb.nat.util;
18
19 import static com.google.common.base.Preconditions.checkArgument;
20
21 import com.google.common.annotations.VisibleForTesting;
22 import com.google.common.base.Optional;
23 import io.fd.honeycomb.translate.MappingContext;
24 import io.fd.honeycomb.translate.vpp.util.Ipv4Translator;
25 import io.fd.vpp.jvpp.snat.dto.SnatStaticMappingDetails;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.List;
29 import javax.annotation.Nonnull;
30 import javax.annotation.concurrent.ThreadSafe;
31 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.naming.context.rev160513.Contexts;
32 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.NatMappingEntryCtxAugmentation;
33 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.NatMappingEntryContext;
34 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.NatInstance;
35 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.NatInstanceKey;
36 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.MappingTable;
37 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntry;
38 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntryBuilder;
39 import org.opendaylight.yang.gen.v1.urn.honeycomb.params.xml.ns.yang.nat.context.rev161214.mapping.entry.context.attributes.nat.mapping.entry.context.nat.instance.mapping.table.MappingEntryKey;
40 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
41 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
42 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45
46 /**
47  * Context tracker for Nat Mapping entries.
48  */
49 @ThreadSafe
50 public class MappingEntryContext implements Ipv4Translator {
51
52     private static final Logger LOG = LoggerFactory.getLogger(MappingEntryContext.class);
53
54     /**
55      * Add mapping entry to index mapping to context.
56      */
57     public synchronized void addEntry(final long natInstanceId,
58                                       final long entryId,
59                                       @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
60                                       @Nonnull final MappingContext mappingContext) {
61         final InstanceIdentifier<MappingEntry> id = getId(natInstanceId, entryToKey(entry));
62         checkArgument(!containsEntry(natInstanceId, entry, mappingContext), "Mapping for %s already present", id);
63         mappingContext.put(id, toCtxMapEntry(entry, entryId));
64     }
65
66     /**
67      * Check whether mapping entry to index mapping already exists in context.
68      */
69     public synchronized boolean containsEntry(final long natInstanceId,
70                                               @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
71                                               @Nonnull final MappingContext mappingContext) {
72         final InstanceIdentifier<MappingEntry> id = getId(natInstanceId, entryToKey(entry));
73         return mappingContext.read(id).isPresent();
74     }
75
76     @VisibleForTesting
77     static InstanceIdentifier<MappingEntry> getId(final Long natInstanceId, final MappingEntryKey key) {
78         return getTableId(natInstanceId).child(MappingEntry.class, key);
79     }
80
81     @VisibleForTesting
82     static InstanceIdentifier<MappingTable> getTableId(final long natInstanceId) {
83         return InstanceIdentifier.create(Contexts.class)
84                 .augmentation(NatMappingEntryCtxAugmentation.class)
85                 .child(NatMappingEntryContext.class)
86                 .child(NatInstance.class, new NatInstanceKey(natInstanceId))
87                 .child(MappingTable.class);
88     }
89
90     @VisibleForTesting
91     static MappingEntryKey entryToKey(
92             final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry) {
93         // Only IPv4
94         return new MappingEntryKey(new IpAddress(entry.getExternalSrcAddress()), entry.getInternalSrcAddress());
95     }
96
97     private MappingEntryKey entryToKey(final SnatStaticMappingDetails entry) {
98         // Only IPv4
99         return new MappingEntryKey(
100                 new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(entry.externalIpAddress))),
101                 new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(entry.localIpAddress))));
102     }
103
104     private boolean equalEntries(final SnatStaticMappingDetails detail, final MappingEntry ctxMappingEntry) {
105         final IpAddress internalAddrFromDetails =
106                 new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(detail.localIpAddress)));
107         // Only IPv4
108         if (!ctxMappingEntry.getInternal().equals(internalAddrFromDetails)) {
109             return false;
110         }
111         // Only IPv4
112         final IpAddress externalAddrFromDetails =
113                 new IpAddress(new Ipv4Address(arrayToIpv4AddressNoZoneReversed(detail.externalIpAddress)));
114         if (!ctxMappingEntry.getExternal().equals(externalAddrFromDetails)) {
115             return false;
116         }
117         return true;
118     }
119
120     @VisibleForTesting
121     static MappingEntry toCtxMapEntry(
122             @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
123             final long entryId) {
124         return new MappingEntryBuilder()
125                 .setKey(entryToKey(entry))
126                 .setIndex(entryId)
127                 .build();
128     }
129
130     private MappingEntry toCtxMapEntry(@Nonnull final SnatStaticMappingDetails details, final long entryId) {
131         return new MappingEntryBuilder()
132                 .setKey(entryToKey(details))
133                 .setIndex(entryId)
134                 .build();
135     }
136
137     /**
138      * Delete mapping of mapping entry to index from context.
139      */
140     public synchronized void removeEntry(final long natInstanceId,
141                                          @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
142                                          @Nonnull final MappingContext mappingContext) {
143         mappingContext.delete(getId(natInstanceId, entryToKey(entry)));
144     }
145
146     /**
147      * Find specific details in provided collection identified with provided index.
148      */
149     public synchronized SnatStaticMappingDetails findDetails(@Nonnull final List<SnatStaticMappingDetails> details,
150                                                              final long natInstanceId, final long idx,
151                                                              @Nonnull final MappingContext mappingContext) {
152         // Find mapping entry for Index
153         final MappingEntry ctxMappingEntry = mappingContext.read(getTableId(natInstanceId))
154                 .transform(MappingTable::getMappingEntry)
155                 .or(Collections.emptyList())
156                 .stream()
157                 .filter(entry -> entry.getIndex() == idx)
158                 .findFirst()
159                 .orElseThrow(() -> new IllegalStateException("Unable to find context mapping for nat-instance: "
160                         + natInstanceId + " and ID: " + idx));
161
162         // Find which details matches the context stored entry under index
163         return details.stream()
164                 .filter(detail -> equalEntries(detail, ctxMappingEntry))
165                 .findFirst()
166                 .orElseThrow(() -> new IllegalStateException("Unable to match mapping for nat-instance: "
167                         + natInstanceId + " and match: " + ctxMappingEntry + " in: " + details));
168     }
169
170     /**
171      * Get index for a mapping entry details or create an artificial one.
172      */
173     public synchronized long getStoredOrArtificialIndex(final Long natInstanceId,
174                                                         @Nonnull final SnatStaticMappingDetails details,
175                                                         @Nonnull final MappingContext mappingContext) {
176         return mappingContext.read(getId(natInstanceId, entryToKey(details)))
177                 .transform(MappingEntry::getIndex)
178                 .or(() -> getArtificialId(details, natInstanceId, mappingContext));
179     }
180
181     /**
182      * Get index for a stored mapping entry.
183      */
184     public synchronized Optional<Long> getStoredIndex(final long natInstanceId,
185                                                       @Nonnull final org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.nat.rev150908.nat.config.nat.instances.nat.instance.mapping.table.MappingEntry entry,
186                                                       @Nonnull final MappingContext mappingContext) {
187         return mappingContext.read(getId(natInstanceId, entryToKey(entry)))
188                 .transform(MappingEntry::getIndex);
189     }
190
191     private long getArtificialId(final SnatStaticMappingDetails details, final Long natInstanceId,
192                                  final MappingContext mappingContext) {
193         LOG.trace("Assigning artificial ID for {}", details);
194         final long artificialIdx = findFreeIndex(natInstanceId, mappingContext);
195         LOG.debug("Artificial ID for {} assigned as: {}", details, artificialIdx);
196         mappingContext.put(getId(natInstanceId, entryToKey(details)), toCtxMapEntry(details, artificialIdx));
197         return artificialIdx;
198     }
199
200     private long findFreeIndex(final long natInstanceId, final MappingContext mappingContext) {
201         return mappingContext.read(getTableId(natInstanceId))
202                 .transform(MappingTable::getMappingEntry)
203                 .or(Collections.emptyList())
204                 .stream()
205                 .map(MappingEntry::getIndex)
206                 .max(Comparator.naturalOrder())
207                 .map(i -> i + 1)
208                 .orElse(0L);
209     }
210 }