Add radix trie to HashMapDb for fast IP LPM
[lispflowmapping.git] / mappingservice / mapcache / src / main / java / org / opendaylight / lispflowmapping / mapcache / MultiTableMapCache.java
1 /*
2  * Copyright (c) 2014 Contextream, 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.mapcache;
10
11 import java.util.Date;
12 import java.util.Map;
13
14 import org.opendaylight.lispflowmapping.interfaces.dao.ILispDAO;
15 import org.opendaylight.lispflowmapping.interfaces.dao.IRowVisitor;
16 import org.opendaylight.lispflowmapping.interfaces.dao.MappingEntry;
17 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
18 import org.opendaylight.lispflowmapping.interfaces.mapcache.IMapCache;
19 import org.opendaylight.lispflowmapping.lisp.util.MaskUtil;
20 import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
21 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.mapping.authkey.container.MappingAuthkey;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Multi table map-cache that works with 'simple' and SourceDest LCAF addresses (see lisp-proto.yang). It can do longest
29  * prefix matching for IP and SourceDest LCAF addresses. In case of the latter, it uses two tables, one for dst and
30  * another for source, queried and populated in this exact order.
31  *
32  * @author Florin Coras
33  *
34  */
35 public class MultiTableMapCache implements IMapCache {
36     private static final Logger LOG = LoggerFactory.getLogger(MultiTableMapCache.class);
37     private ILispDAO dao;
38
39     public MultiTableMapCache(ILispDAO dao) {
40         this.dao = dao;
41     }
42
43     private ILispDAO getVniTable(Eid eid) {
44         long vni = 0;
45         if (eid.getVirtualNetworkId() == null) {
46             vni = 0;
47         } else {
48             vni = eid.getVirtualNetworkId().getValue();
49         }
50         return (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
51     }
52
53     private ILispDAO getOrInstantiateVniTable(Eid eid) {
54         long vni = 0;
55         if (eid.getVirtualNetworkId() == null) {
56             vni = 0;
57         } else {
58             vni = eid.getVirtualNetworkId().getValue();
59         }
60         ILispDAO table = (ILispDAO) dao.getSpecific(vni, SubKeys.VNI);
61         if (table == null) {
62             table = dao.putNestedTable(vni, SubKeys.VNI);
63         }
64         return table;
65     }
66
67     public void addMapping(Eid key, Object value, boolean shouldOverwrite, boolean shouldMerge) {
68         Eid eid = MaskUtil.normalize(key);
69         ILispDAO table = getOrInstantiateVniTable(key);
70
71         if (eid.getAddress() instanceof SourceDestKey) {
72             Eid srcKey = SourceDestKeyHelper.getSrcBinary(eid);
73             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(eid, table);
74             srcDstDao.put(srcKey, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
75             srcDstDao.put(srcKey, new MappingEntry<>(SubKeys.RECORD, value));
76         } else {
77             table.put(eid, new MappingEntry<>(SubKeys.REGDATE, new Date(System.currentTimeMillis())));
78             table.put(eid, new MappingEntry<>(SubKeys.RECORD, value));
79         }
80     }
81
82     private Object getMappingExactSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
83         Map<String, ?> daoEntry = dao.get(dstEid);
84         if (daoEntry != null) {
85             // try SrcDst eid lookup
86             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
87             if (srcEid != null && srcDstDao != null) {
88                 return srcDstDao.getSpecific(srcEid, SubKeys.RECORD);
89             }
90             // if lookup fails, return whatever is found for dst eid
91             return daoEntry.get(SubKeys.RECORD);
92         }
93         return null;
94     }
95
96     // Returns the mapping corresponding to the longest prefix match for eid.
97     // eid must be a simple (maskable or not) address
98     private Object getMappingLpmEid(Eid eid, ILispDAO dao) {
99         if (eid == null) {
100             return null;
101         }
102         Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(eid));
103         if (daoEntry != null) {
104             return daoEntry.get(SubKeys.RECORD);
105         } else {
106             return null;
107         }
108     }
109
110     // Returns a mapping corresponding to either the longest prefix match for both dstEid and srcEid,
111     // if a SourceDest mapping exists, or to dstEid
112     private Object getMappingLpmSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
113         Map<String, ?> daoEntry = dao.getBest(MaskUtil.normalize(dstEid));
114         if (daoEntry != null) {
115             // try SrcDst eid lookup
116             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
117             if (srcDstDao != null) {
118                 Object mapping = getMappingLpmEid(srcEid, srcDstDao);
119                 if (mapping!= null) {
120                     return mapping;
121                 }
122             }
123
124             // if lookup fails, return whatever is found for dst eid
125             return daoEntry.get(SubKeys.RECORD);
126         }
127         return null;
128     }
129
130     public Object getMapping(Eid srcEid, Eid dstEid) {
131         if (dstEid == null) {
132             return null;
133         }
134
135         ILispDAO table = getVniTable(dstEid);
136         if (table == null) {
137             return null;
138         }
139
140         // a map-request for an actual SrcDst LCAF, ignore src eid
141         if (dstEid.getAddress() instanceof SourceDestKey) {
142             Eid srcAddr = SourceDestKeyHelper.getSrcBinary(dstEid);
143             Eid dstAddr = SourceDestKeyHelper.getDstBinary(dstEid);
144             return getMappingLpmSD(srcAddr, dstAddr, table);
145         }
146
147         // potential map-request for SrcDst LCAF from non SrcDst capable devices
148         return getMappingLpmSD(srcEid, dstEid, table);
149     }
150
151     @Override
152     public Eid getWidestNegativeMapping(Eid key) {
153         return dao.getWidestNegativePrefix(key);
154     }
155
156     public void removeMapping(Eid eid, boolean overwrite) {
157         Eid key = MaskUtil.normalize(eid);
158         ILispDAO table = getVniTable(key);
159         if (table == null) {
160             return;
161         }
162
163         if (key.getAddress() instanceof SourceDestKey) {
164             ILispDAO db = getSDInnerDao(key, table);
165             if (db != null) {
166                 db.removeSpecific(SourceDestKeyHelper.getSrcBinary(key),
167                         SubKeys.RECORD);
168             }
169         } else {
170             table.removeSpecific(key, SubKeys.RECORD);
171         }
172     }
173
174     public void addAuthenticationKey(Eid eid, MappingAuthkey authKey) {
175         Eid key = MaskUtil.normalize(eid);
176         ILispDAO table = getOrInstantiateVniTable(key);
177
178         if (key.getAddress() instanceof SourceDestKey) {
179             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(key, table);
180             srcDstDao.put(SourceDestKeyHelper.getSrcBinary(key), new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
181         } else {
182             table.put(key, new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
183         }
184     }
185
186     private MappingAuthkey getAuthKeyLpm(Eid prefix, ILispDAO db) {
187         short maskLength = MaskUtil.getMaskForAddress(prefix.getAddress());
188         while (maskLength >= 0) {
189             Eid key = MaskUtil.normalize(prefix, maskLength);
190             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
191             if (password != null && password instanceof MappingAuthkey) {
192                 return (MappingAuthkey) password;
193             }
194             maskLength -= 1;
195         }
196         return null;
197     }
198
199     public MappingAuthkey getAuthenticationKey(Eid eid) {
200         ILispDAO table = getVniTable(eid);
201         if (table == null) {
202             return null;
203         }
204
205         if (MaskUtil.isMaskable(eid.getAddress())) {
206             if (eid.getAddress() instanceof SourceDestKey) {
207                 // NOTE: this is an exact match, not a longest prefix match
208                 ILispDAO srcDstDao = getSDInnerDao(eid, table);
209                 if (srcDstDao != null) {
210                     return getAuthKeyLpm(SourceDestKeyHelper.getSrcBinary(eid), srcDstDao);
211                 }
212                 return null;
213             } else {
214                 return getAuthKeyLpm(eid, table);
215             }
216         } else {
217             Eid key = MaskUtil.normalize(eid);
218             Object password = table.getSpecific(key, SubKeys.AUTH_KEY);
219             if (password != null && password instanceof MappingAuthkey) {
220                 return (MappingAuthkey) password;
221             } else {
222                 LOG.warn("Failed to find password!");
223                 return null;
224             }
225         }
226     }
227
228     public void removeAuthenticationKey(Eid eid) {
229         Eid key = MaskUtil.normalize(eid);
230         ILispDAO table = getVniTable(key);
231         if (table == null) {
232             return;
233         }
234
235         if (key.getAddress() instanceof SourceDestKey) {
236             ILispDAO srcDstDao = getSDInnerDao(key, table);
237             if (srcDstDao != null) {
238                 srcDstDao.removeSpecific(key, SubKeys.AUTH_KEY);
239             }
240         } else {
241             table.removeSpecific(key, SubKeys.AUTH_KEY);
242         }
243     }
244
245     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
246     // This method returns the DAO associated to a dst or creates it if it doesn't exist.
247     private ILispDAO getOrInstantiateSDInnerDao(Eid address, ILispDAO dao) {
248         Eid dstKey = SourceDestKeyHelper.getDstBinary(address);
249         ILispDAO srcDstDao = (ILispDAO) dao.getSpecific(dstKey, SubKeys.LCAF_SRCDST);
250         if (srcDstDao == null) {
251             // inserts nested table for source
252             srcDstDao = dao.putNestedTable(dstKey, SubKeys.LCAF_SRCDST);
253         }
254         return srcDstDao;
255     }
256
257     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
258     // This method returns the DAO associated to dst or null if it doesn't exist.
259     private ILispDAO getSDInnerDao(Eid address, ILispDAO dao) {
260         return (ILispDAO) dao.getSpecific(SourceDestKeyHelper.getDstBinary(address), SubKeys.LCAF_SRCDST);
261     }
262
263     public String printMappings() {
264         final StringBuffer sb = new StringBuffer();
265         sb.append("Keys\tValues\n");
266         final IRowVisitor innerVisitor = (new IRowVisitor() {
267             String lastKey = "";
268
269             public void visitRow(Object keyId, String valueKey, Object value) {
270                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
271                 if (!lastKey.equals(key)) {
272                     sb.append("\n" + key + "\t");
273                 }
274                 sb.append(valueKey + "=" + value + "\t");
275                 lastKey = key;
276             }
277         });
278         final IRowVisitor vniVisitor = (new IRowVisitor() {
279             String lastKey = "";
280
281             public void visitRow(Object keyId, String valueKey, Object value) {
282                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
283                 if (!lastKey.equals(key)) {
284                     sb.append(key + "\t");
285                 }
286                 if ((valueKey.equals(SubKeys.LCAF_SRCDST))) {
287                     sb.append(valueKey + "= { ");
288                     ((ILispDAO)value).getAll(innerVisitor);
289                     sb.append("}\t");
290                 } else {
291                     sb.append(valueKey + "=" + value + "\t");
292                 }
293                 lastKey = key;
294             }
295         });
296         dao.getAll(new IRowVisitor() {
297             String lastKey = "";
298
299             public void visitRow(Object keyId, String valueKey, Object value) {
300                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
301                 if (!lastKey.equals(key)) {
302                     sb.append("\n" + key + "\t");
303                 }
304                 if (valueKey.equals(SubKeys.VNI)) {
305                     sb.append(valueKey + "= { ");
306                     ((ILispDAO)value).getAll(vniVisitor);
307                     sb.append("}\t");
308                 } else {
309                     sb.append(valueKey + "=" + value + "\t");
310                 }
311                 lastKey = key;
312             }
313         });
314         sb.append("\n");
315         return sb.toString();
316     }
317
318     @Override
319     public void updateMappingRegistration(Eid key, Long timestamp) {
320
321     }
322
323     @Override
324     public void addData(Eid eid, String subKey, Object data) {
325         Eid key = MaskUtil.normalize(eid);
326         ILispDAO table = getOrInstantiateVniTable(key);
327
328         if (key.getAddress() instanceof SourceDestKey) {
329             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(key, table);
330             srcDstDao.put(SourceDestKeyHelper.getSrcBinary(key), new MappingEntry<Object>(subKey, data));
331         } else {
332             table.put(key, new MappingEntry<Object>(subKey, data));
333         }
334     }
335
336     @Override
337     public Object getData(Eid eid, String subKey) {
338         Eid key = MaskUtil.normalize(eid);
339         ILispDAO table = getVniTable(key);
340         if (table == null) {
341             return null;
342         }
343
344         if (key.getAddress() instanceof SourceDestKey) {
345             ILispDAO srcDstDao = getSDInnerDao(key, table);
346             return srcDstDao.getSpecific(SourceDestKeyHelper.getSrcBinary(key), subKey);
347         } else {
348             return table.getSpecific(key, subKey);
349         }
350     }
351
352     @Override
353     public void removeData(Eid eid, String subKey) {
354         Eid key = MaskUtil.normalize(eid);
355         ILispDAO table = getVniTable(key);
356         if (table == null) {
357             return;
358         }
359         if (key.getAddress() instanceof SourceDestKey) {
360             ILispDAO db = getSDInnerDao(key, table);
361             if (db != null) {
362                 db.removeSpecific(SourceDestKeyHelper.getSrcBinary(key), subKey);
363             }
364         } else {
365             table.removeSpecific(key, subKey);
366         }
367     }
368 }