View Javadoc

1   /*
2    * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6    * and is available at http://www.eclipse.org/legal/epl-v10.html
7    */
8   
9   package org.opendaylight.lispflowmapping.implementation.mapcache;
10  
11  import java.util.AbstractMap.SimpleImmutableEntry;
12  import java.nio.ByteBuffer;
13  import java.util.ArrayList;
14  import java.util.Date;
15  import java.util.HashSet;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Set;
19  
20  import org.opendaylight.lispflowmapping.implementation.config.ConfigIni;
21  import org.opendaylight.lispflowmapping.implementation.util.MappingMergeUtil;
22  import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
23  import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
24  import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
25  import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
26  import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
27  import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
28  import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
29  import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv4Prefix;
30  import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.Ipv6Prefix;
31  import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
32  import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
33  import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.authkey.container.MappingAuthkey;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  /**
38   * Simple map-cache that works with 'simple' addresses (see lisp-proto.yang). It can do longest prefix matching for IP
39   * addresses.
40   *
41   * @author Florin Coras
42   * @author Lorand Jakab
43   *
44   */
45  public class SimpleMapCache implements IMapCache {
46      private static final Logger LOG = LoggerFactory.getLogger(SimpleMapCache.class);
47      private ILispDAO dao;
48  
49      public SimpleMapCache(ILispDAO dao) {
50          this.dao = dao;
51      }
52  
53      private ILispDAO getVniTable(Eid eid) {
54          long vni = 0;
55          if (eid.getVirtualNetworkId() == null) {
56              vni = 0;
57          } else {
58              vni = eid.getVirtualNetworkId().getValue();
59          }
60          return (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
61      }
62  
63      private ILispDAO getOrInstantiateVniTable(Eid eid) {
64          long vni = 0;
65          if (eid.getVirtualNetworkId() == null) {
66              vni = 0;
67          } else {
68              vni = eid.getVirtualNetworkId().getValue();
69          }
70          ILispDAO table = (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
71          if (table == null) {
72              table = dao.putNestedTable(vni, SubKeys.VNI);
73          }
74          return table;
75      }
76  
77      private ILispDAO getXtrIdTable(Eid eid, ILispDAO dao) {
78          return (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
79      }
80  
81      private ILispDAO getOrInstantiateXtrIdTable(Eid eid, ILispDAO dao) {
82          ILispDAO table = (ILispDAO) dao.getSpecific(eid, SubKeys.XTRID_RECORDS);
83          if (table == null) {
84              table = dao.putNestedTable(eid, SubKeys.XTRID_RECORDS);
85          }
86          return table;
87      }
88  
89      public static Object deserializeBytes(byte[] data) {
90          return ByteBuffer.wrap(data);
91      }
92  
93      private void removeExpiredXtrIdTableEntries(ILispDAO xtrIdDao, List<byte[]> expiredMappings) {
94          for (byte[] xtrId : expiredMappings) {
95              xtrIdDao.removeSpecific(deserializeBytes(xtrId), SubKeys.RECORD);
96          }
97      }
98  
99      public void addMapping(Eid key, Object value, boolean shouldOverwrite) {
100         if (value == null) {
101             LOG.warn("addMapping() called with null 'value', ignoring");
102             return;
103         }
104 
105         if (!(value instanceof MappingRecord)) {
106             LOG.warn("addMapping() called with a 'value' that is not a 'MappingRecord', ignoring");
107             return;
108         }
109 
110         MappingRecord record = (MappingRecord) value;
111         if (record.getXtrId() == null && !shouldOverwrite && ConfigIni.getInstance().mappingMergeIsSet()) {
112             LOG.warn("addMapping() called will null xTR-ID in MappingRecord, while merge is set, ignoring");
113             return;
114         }
115 
116         Date regdate = new Date(record.getTimestamp());     // The serializer always sets it
117         Eid eid = MaskUtil.normalize(key);
118         ILispDAO table = getOrInstantiateVniTable(key);
119 
120         ILispDAO xtrIdDao = null;
121         if (!shouldOverwrite) {
122             xtrIdDao = getOrInstantiateXtrIdTable(eid, table);
123             xtrIdDao.put(deserializeBytes(record.getXtrId()), new MappingEntry<>(SubKeys.RECORD, value));
124         }
125 
126         if (ConfigIni.getInstance().mappingMergeIsSet()) {
127             List<byte[]> expiredMappings = new ArrayList<byte[]>();
128             Set<IpAddress> sourceRlocs = new HashSet<IpAddress>();
129             MappingRecord mergedEntry = MappingMergeUtil.mergeXtrIdMappings(getXtrIdMappingList(xtrIdDao),
130                     expiredMappings, sourceRlocs);
131             removeExpiredXtrIdTableEntries(xtrIdDao, expiredMappings);
132             if (mergedEntry == null) {
133                 return;
134             }
135             regdate = new Date(mergedEntry.getTimestamp());
136             table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
137             table.put(eid, new MappingEntry<>(SubKeys.RECORD, mergedEntry));
138             table.put(eid, new MappingEntry<>(SubKeys.SRC_RLOCS, sourceRlocs));
139         } else {
140             table.put(eid, new MappingEntry<>(SubKeys.REGDATE, regdate));
141             table.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
142         }
143     }
144 
145     // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
146     // or the exact match otherwise. eid must be a 'simple' address
147     private  Map<String, Object> getDaoEntryBest(Eid eid, ILispDAO dao) {
148         Eid key;
149         if (MaskUtil.isMaskable(eid.getAddress())) {
150             short mask = MaskUtil.getMaskForAddress(eid.getAddress());
151             while (mask > 0) {
152                 key = MaskUtil.normalize(eid, mask);
153                 mask--;
154                 Map<String, Object> entry = dao.get(key);
155                 if (entry != null) {
156                     return entry;
157                 }
158             }
159             return null;
160         } else {
161             key = MaskUtil.normalize(eid);
162             return dao.get(key);
163         }
164     }
165 
166     // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
167     // or the exact match otherwise. eid must be a 'simple' address
168     private SimpleImmutableEntry<Eid, Map<String, ?>> getDaoPairEntryBest(Eid eid, ILispDAO dao) {
169         Eid key;
170         if (MaskUtil.isMaskable(eid.getAddress())) {
171             short mask = MaskUtil.getMaskForAddress(eid.getAddress());
172             while (mask > 0) {
173                 key = MaskUtil.normalize(eid, mask);
174                 mask--;
175                 Map<String, ?> entry = dao.get(key);
176                 if (entry != null) {
177                     return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
178                 }
179             }
180             return null;
181         } else {
182             key = MaskUtil.normalize(eid);
183             Map<String, ?> entry = dao.get(key);
184             if (entry != null) {
185                 return new SimpleImmutableEntry<Eid, Map<String, ?>>(key, entry);
186             } else {
187                 return null;
188             }
189         }
190     }
191 
192     // Returns the list of mappings stored in an xTR-ID DAO
193     private List<Object> getXtrIdMappingList(ILispDAO dao) {
194         if (dao != null) {
195             final List<Object> records = new ArrayList<Object>();
196             dao.getAll(new IRowVisitor() {
197                 public void visitRow(Object keyId, String valueKey, Object value) {
198                     if (valueKey.equals(SubKeys.RECORD)) {
199                         records.add(value);
200                     }
201                 }
202             });
203             return records;
204         }
205         return null;
206     }
207 /*
208     private SimpleImmutableEntry<byte[], Long> getXtrIdMinTimeStamp(ILispDAO dao) {
209         if (dao != null) {
210             // We don't actually need a list here, but the external variable we modify inside the IRowVisitor
211             // implementation has to be final. So we use a List, and it's methods, to modify contents
212             final List<SimpleImmutableEntry<byte[], Long>> entries =
213                     new ArrayList<SimpleImmutableEntry<byte[], Long>>();
214             entries.set(0, new SimpleImmutableEntry<byte[], Long>(null, System.currentTimeMillis()));
215 
216             dao.getAll(new IRowVisitor() {
217                 public void visitRow(Object keyId, String valueKey, Object value) {
218                     if (valueKey.equals(SubKeys.RECORD)) {
219                         byte[] currentXtrId = ((MappingRecord) value).getXtrId();
220                         long currentTimestamp = ((MappingRecord) value).getTimestamp();
221                         // If one of the timestamps is expired, we signal it to the caller by setting the returned
222                         // timestamp to null. The caller will then have to remove that xTR-ID, and do a full merge
223                         // (or decrement)
224                         if (MappingMergeUtil.timestampIsExpired(currentTimestamp)) {
225                             SimpleImmutableEntry<byte[], Long> entry =
226                                     new SimpleImmutableEntry<byte[], Long>(currentXtrId, null);
227                             entries.set(0, entry);
228                             return;
229                         }
230                         if (entries.get(0).getValue() > currentTimestamp) {
231                             SimpleImmutableEntry<byte[], Long> entry =
232                                     new SimpleImmutableEntry<byte[], Long>(currentXtrId, currentTimestamp);
233                             entries.set(0, entry);
234                         }
235                     }
236                 }
237             });
238             return entries.get(0);
239         }
240         return null;
241     }
242 */
243     // Returns the mapping corresponding to the longest prefix match for eid. eid must be a simple (maskable or not)
244     // address
245     private Object getMappingLpmEid(Eid eid, byte[] xtrId, ILispDAO dao) {
246         SimpleImmutableEntry<Eid, Map<String, ?>> daoEntry = getDaoPairEntryBest(eid, dao);
247         if (daoEntry != null) {
248             if (xtrId != null) {
249                 ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.getValue().get(SubKeys.XTRID_RECORDS));
250                 if (xtrIdTable != null) {
251                     MappingRecord xtrIdRecord = (MappingRecord) xtrIdTable.getSpecific(xtrId, SubKeys.RECORD);
252                     if (xtrIdRecord.getTimestamp() != null &&
253                             MappingMergeUtil.timestampIsExpired(xtrIdRecord.getTimestamp())) {
254                         xtrIdTable.removeSpecific(xtrId, SubKeys.RECORD);
255                         return null;
256                     } else {
257                         return xtrIdRecord;
258                     }
259                 } else {
260                     return null;
261                 }
262             } else {
263                 Date timestamp = (Date) daoEntry.getValue().get(SubKeys.REGDATE);
264                 if (timestamp != null && MappingMergeUtil.timestampIsExpired(timestamp)) {
265                     dao.removeSpecific(daoEntry.getKey(), SubKeys.REGDATE);
266                     dao.removeSpecific(daoEntry.getKey(), SubKeys.RECORD);
267                 }
268                 return daoEntry.getValue().get(SubKeys.RECORD);
269             }
270         } else {
271             return null;
272         }
273     }
274 
275     public Object getMapping(Eid srcEid, Eid dstEid, byte[] xtrId) {
276         if (dstEid == null) {
277             return null;
278         }
279 
280         ILispDAO table = getVniTable(dstEid);
281         if (table == null) {
282             return null;
283         }
284         return getMappingLpmEid(dstEid, xtrId, table);
285     }
286 
287     public Object getMapping(Eid srcEid, Eid dstEid) {
288         return getMapping(srcEid, dstEid, null);
289     }
290 
291     public List<Object> getAllXtrIdMappings(Eid eid) {
292         Map<String, ?> daoEntry = getDaoEntryBest(eid, dao);
293         if (daoEntry != null) {
294             ILispDAO xtrIdTable = getXtrIdTable(eid, (ILispDAO) daoEntry.get(SubKeys.XTRID_RECORDS));
295             if (xtrIdTable != null) {
296                 return getXtrIdMappingList(xtrIdTable);
297             }
298         }
299         return null;
300     }
301 
302     public void removeMapping(Eid eid, boolean overwrite) {
303         Eid key = MaskUtil.normalize(eid);
304         ILispDAO table = getVniTable(key);
305         if (table == null) {
306             return;
307         }
308 
309         table.removeSpecific(key, SubKeys.RECORD);
310 
311         if (!overwrite) {
312             ILispDAO xtrIdTable = getXtrIdTable(key, table);
313             if (xtrIdTable != null) {
314                 xtrIdTable.removeSpecific(key, SubKeys.RECORD);
315             }
316         }
317     }
318 
319     public void addAuthenticationKey(Eid eid, MappingAuthkey authKey) {
320         Eid key = MaskUtil.normalize(eid);
321         ILispDAO table = getOrInstantiateVniTable(key);
322         table.put(key, new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
323     }
324 
325     private MappingAuthkey getAuthKeyLpm(Eid prefix, ILispDAO db) {
326         short maskLength = MaskUtil.getMaskForAddress(prefix.getAddress());
327         while (maskLength >= 0) {
328             Eid key = MaskUtil.normalize(prefix, maskLength);
329             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
330             if (password != null && password instanceof MappingAuthkey) {
331                 return (MappingAuthkey) password;
332             }
333             maskLength -= 1;
334         }
335         return null;
336     }
337 
338     public MappingAuthkey getAuthenticationKey(Eid eid) {
339         ILispDAO table = getVniTable(eid);
340         if (table == null) {
341             return null;
342         }
343         if (eid.getAddress() instanceof Ipv4Prefix || eid.getAddress() instanceof Ipv6Prefix) {
344             return getAuthKeyLpm(eid, table);
345         } else {
346             Eid key = MaskUtil.normalize(eid);
347             Object password = table.getSpecific(key, SubKeys.AUTH_KEY);
348             if (password != null && password instanceof MappingAuthkey) {
349                 return (MappingAuthkey) password;
350             } else {
351                 LOG.warn("Failed to find password!");
352                 return null;
353             }
354         }
355     }
356 
357     public void removeAuthenticationKey(Eid eid) {
358         Eid key = MaskUtil.normalize(eid);
359         ILispDAO table = getVniTable(key);
360         if (table == null) {
361             return;
362         }
363         table.removeSpecific(key, SubKeys.AUTH_KEY);
364     }
365 
366     public String printMappings() {
367         final StringBuffer sb = new StringBuffer();
368         sb.append("Keys\tValues\n");
369 
370         final IRowVisitor innerVisitor = (new IRowVisitor() {
371             String lastKey = "";
372 
373             public void visitRow(Object keyId, String valueKey, Object value) {
374                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
375                 if (!lastKey.equals(key)) {
376                     sb.append("\n" + key + "\t");
377                 }
378                 sb.append(valueKey + "=" + value + "\t");
379                 lastKey = key;
380             }
381         });
382 
383         dao.getAll(new IRowVisitor() {
384             String lastKey = "";
385 
386             public void visitRow(Object keyId, String valueKey, Object value) {
387                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
388                 if (!lastKey.equals(key)) {
389                     sb.append("\n" + key + "\t");
390                 }
391                 if (valueKey.equals(SubKeys.VNI)) {
392                     sb.append(valueKey + "= { ");
393                     ((ILispDAO)value).getAll(innerVisitor);
394                     sb.append("}\t");
395                 } else {
396                     sb.append(valueKey + "=" + value + "\t");
397                 }
398                 lastKey = key;
399             }
400         });
401         sb.append("\n");
402         return sb.toString();
403     }
404 
405     @Override
406     public void updateMappingRegistration(Eid eid) {
407         ILispDAO table = getVniTable(eid);
408         if (table == null) {
409             return;
410         }
411         Map<String, Object> daoEntry = getDaoEntryBest(eid, table);
412         if (daoEntry != null) {
413             daoEntry.put(SubKeys.REGDATE, new Date(System.currentTimeMillis()));
414         }
415     }
416 
417     @Override
418     public void addData(Eid eid, String subKey, Object data) {
419         Eid key = MaskUtil.normalize(eid);
420         ILispDAO table = getOrInstantiateVniTable(key);
421         table.put(key, new MappingEntry<>(subKey, data));
422     }
423 
424     @Override
425     public Object getData(Eid eid, String subKey) {
426         ILispDAO table = getOrInstantiateVniTable(eid);
427         if (table == null) {
428             return null;
429         }
430         Eid key = MaskUtil.normalize(eid);
431         return table.getSpecific(key, subKey);
432     }
433 
434     @Override
435     public void removeData(Eid eid, String subKey) {
436         ILispDAO table = getOrInstantiateVniTable(eid);
437         if (table == null) {
438             return;
439         }
440         Eid key = MaskUtil.normalize(eid);
441         table.removeSpecific(key, subKey);
442     }
443 }