Auto-convert String IPs in Mapping
[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     // Method returns the DAO entry (hash) corresponding to either the longest prefix match of eid, if eid is maskable,
83     // or the exact match otherwise. eid must be a 'simple' address
84     private Map<String, ?> getDaoEntryBest(Eid eid, ILispDAO dao) {
85         Eid key;
86         if (MaskUtil.isMaskable(eid.getAddress())) {
87             short mask = MaskUtil.getMaskForAddress(eid.getAddress());
88             while (mask > 0) {
89                 key = MaskUtil.normalize(eid, mask);
90                 mask--;
91                 Map<String, ?> entry = dao.get(key);
92                 if (entry != null) {
93                     return entry;
94                 }
95             }
96             return null;
97         } else {
98             key = MaskUtil.normalize(eid);
99             Map<String, ?> entry = dao.get(key);
100             return entry;
101         }
102     }
103
104     private Object getMappingExactSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
105         Map<String, ?> daoEntry = dao.get(dstEid);
106         if (daoEntry != null) {
107             // try SrcDst eid lookup
108             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
109             if (srcEid != null && srcDstDao != null) {
110                 return srcDstDao.getSpecific(srcEid, SubKeys.RECORD);
111             }
112             // if lookup fails, return whatever is found for dst eid
113             return daoEntry.get(SubKeys.RECORD);
114         }
115         return null;
116     }
117
118     // Returns the mapping corresponding to the longest prefix match for eid.
119     // eid must be a simple (maskable or not) address
120     private Object getMappingLpmEid(Eid eid, ILispDAO dao) {
121         if (eid == null) {
122             return null;
123         }
124         Eid key = MaskUtil.normalize(eid);
125         Map<String, ?> daoEntry = getDaoEntryBest(key, dao);
126         if (daoEntry != null) {
127             return daoEntry.get(SubKeys.RECORD);
128         } else {
129             return null;
130         }
131     }
132
133     // Returns a mapping corresponding to either the longest prefix match for both dstEid and srcEid,
134     // if a SourceDest mapping exists, or to dstEid
135     private Object getMappingLpmSD(Eid srcEid, Eid dstEid, ILispDAO dao) {
136         Map<String, ?> daoEntry = getDaoEntryBest(dstEid, dao);
137         if (daoEntry != null) {
138             // try SrcDst eid lookup
139             ILispDAO srcDstDao = (ILispDAO) daoEntry.get(SubKeys.LCAF_SRCDST);
140             if (srcDstDao != null) {
141                 Object mapping = getMappingLpmEid(srcEid, srcDstDao);
142                 if (mapping!= null) {
143                     return mapping;
144                 }
145             }
146
147             // if lookup fails, return whatever is found for dst eid
148             return daoEntry.get(SubKeys.RECORD);
149         }
150         return null;
151     }
152
153     public Object getMapping(Eid srcEid, Eid dstEid) {
154         if (dstEid == null) {
155             return null;
156         }
157
158         ILispDAO table = getVniTable(dstEid);
159         if (table == null) {
160             return null;
161         }
162
163         // a map-request for an actual SrcDst LCAF, ignore src eid
164         if (dstEid.getAddress() instanceof SourceDestKey) {
165             Eid srcAddr = SourceDestKeyHelper.getSrcBinary(dstEid);
166             Eid dstAddr = SourceDestKeyHelper.getDstBinary(dstEid);
167             return getMappingLpmSD(srcAddr, dstAddr, table);
168         }
169
170         // potential map-request for SrcDst LCAF from non SrcDst capable devices
171         return getMappingLpmSD(srcEid, dstEid, table);
172     }
173
174     public void removeMapping(Eid eid, boolean overwrite) {
175         Eid key = MaskUtil.normalize(eid);
176         ILispDAO table = getVniTable(key);
177         if (table == null) {
178             return;
179         }
180
181         if (key.getAddress() instanceof SourceDestKey) {
182             ILispDAO db = getSDInnerDao(key, table);
183             if (db != null) {
184                 db.removeSpecific(SourceDestKeyHelper.getSrcBinary(key),
185                         SubKeys.RECORD);
186             }
187         } else {
188             table.removeSpecific(key, SubKeys.RECORD);
189         }
190     }
191
192     public void addAuthenticationKey(Eid eid, MappingAuthkey authKey) {
193         Eid key = MaskUtil.normalize(eid);
194         ILispDAO table = getOrInstantiateVniTable(key);
195
196         if (key.getAddress() instanceof SourceDestKey) {
197             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(key, table);
198             srcDstDao.put(SourceDestKeyHelper.getSrcBinary(key), new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
199         } else {
200             table.put(key, new MappingEntry<>(SubKeys.AUTH_KEY, authKey));
201         }
202     }
203
204     private MappingAuthkey getAuthKeyLpm(Eid prefix, ILispDAO db) {
205         short maskLength = MaskUtil.getMaskForAddress(prefix.getAddress());
206         while (maskLength >= 0) {
207             Eid key = MaskUtil.normalize(prefix, maskLength);
208             Object password = db.getSpecific(key, SubKeys.AUTH_KEY);
209             if (password != null && password instanceof MappingAuthkey) {
210                 return (MappingAuthkey) password;
211             }
212             maskLength -= 1;
213         }
214         return null;
215     }
216
217     public MappingAuthkey getAuthenticationKey(Eid eid) {
218         ILispDAO table = getVniTable(eid);
219         if (table == null) {
220             return null;
221         }
222
223         if (MaskUtil.isMaskable(eid.getAddress())) {
224             if (eid.getAddress() instanceof SourceDestKey) {
225                 // NOTE: this is an exact match, not a longest prefix match
226                 ILispDAO srcDstDao = getSDInnerDao(eid, table);
227                 if (srcDstDao != null) {
228                     return getAuthKeyLpm(SourceDestKeyHelper.getSrcBinary(eid), srcDstDao);
229                 }
230                 return null;
231             } else {
232                 return getAuthKeyLpm(eid, table);
233             }
234         } else {
235             Eid key = MaskUtil.normalize(eid);
236             Object password = table.getSpecific(key, SubKeys.AUTH_KEY);
237             if (password != null && password instanceof MappingAuthkey) {
238                 return (MappingAuthkey) password;
239             } else {
240                 LOG.warn("Failed to find password!");
241                 return null;
242             }
243         }
244     }
245
246     public void removeAuthenticationKey(Eid eid) {
247         Eid key = MaskUtil.normalize(eid);
248         ILispDAO table = getVniTable(key);
249         if (table == null) {
250             return;
251         }
252
253         if (key.getAddress() instanceof SourceDestKey) {
254             ILispDAO srcDstDao = getSDInnerDao(key, table);
255             if (srcDstDao != null) {
256                 srcDstDao.removeSpecific(key, SubKeys.AUTH_KEY);
257             }
258         } else {
259             table.removeSpecific(key, SubKeys.AUTH_KEY);
260         }
261     }
262
263     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
264     // This method returns the DAO associated to a dst or creates it if it doesn't exist.
265     private ILispDAO getOrInstantiateSDInnerDao(Eid address, ILispDAO dao) {
266         Eid dstKey = SourceDestKeyHelper.getDstBinary(address);
267         ILispDAO srcDstDao = (ILispDAO) dao.getSpecific(dstKey, SubKeys.LCAF_SRCDST);
268         if (srcDstDao == null) {
269             // inserts nested table for source
270             srcDstDao = dao.putNestedTable(dstKey, SubKeys.LCAF_SRCDST);
271         }
272         return srcDstDao;
273     }
274
275     // SrcDst LCAFs are stored in a 2-tier DAO with dst having priority over src.
276     // This method returns the DAO associated to dst or null if it doesn't exist.
277     private ILispDAO getSDInnerDao(Eid address, ILispDAO dao) {
278         return (ILispDAO) dao.getSpecific(SourceDestKeyHelper.getDstBinary(address), SubKeys.LCAF_SRCDST);
279     }
280
281     public String printMappings() {
282         final StringBuffer sb = new StringBuffer();
283         sb.append("Keys\tValues\n");
284         final IRowVisitor innerVisitor = (new IRowVisitor() {
285             String lastKey = "";
286
287             public void visitRow(Object keyId, String valueKey, Object value) {
288                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
289                 if (!lastKey.equals(key)) {
290                     sb.append("\n" + key + "\t");
291                 }
292                 sb.append(valueKey + "=" + value + "\t");
293                 lastKey = key;
294             }
295         });
296         final IRowVisitor vniVisitor = (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(key + "\t");
303                 }
304                 if ((valueKey.equals(SubKeys.LCAF_SRCDST))) {
305                     sb.append(valueKey + "= { ");
306                     ((ILispDAO)value).getAll(innerVisitor);
307                     sb.append("}\t");
308                 } else {
309                     sb.append(valueKey + "=" + value + "\t");
310                 }
311                 lastKey = key;
312             }
313         });
314         dao.getAll(new IRowVisitor() {
315             String lastKey = "";
316
317             public void visitRow(Object keyId, String valueKey, Object value) {
318                 String key = keyId.getClass().getSimpleName() + "#" + keyId;
319                 if (!lastKey.equals(key)) {
320                     sb.append("\n" + key + "\t");
321                 }
322                 if (valueKey.equals(SubKeys.VNI)) {
323                     sb.append(valueKey + "= { ");
324                     ((ILispDAO)value).getAll(vniVisitor);
325                     sb.append("}\t");
326                 } else {
327                     sb.append(valueKey + "=" + value + "\t");
328                 }
329                 lastKey = key;
330             }
331         });
332         sb.append("\n");
333         return sb.toString();
334     }
335
336     @Override
337     public void updateMappingRegistration(Eid key, Long timestamp) {
338
339     }
340
341     @Override
342     public void addData(Eid eid, String subKey, Object data) {
343         Eid key = MaskUtil.normalize(eid);
344         ILispDAO table = getOrInstantiateVniTable(key);
345
346         if (key.getAddress() instanceof SourceDestKey) {
347             ILispDAO srcDstDao = getOrInstantiateSDInnerDao(key, table);
348             srcDstDao.put(SourceDestKeyHelper.getSrcBinary(key), new MappingEntry<Object>(subKey, data));
349         } else {
350             table.put(key, new MappingEntry<Object>(subKey, data));
351         }
352     }
353
354     @Override
355     public Object getData(Eid eid, String subKey) {
356         Eid key = MaskUtil.normalize(eid);
357         ILispDAO table = getVniTable(key);
358         if (table == null) {
359             return null;
360         }
361
362         if (key.getAddress() instanceof SourceDestKey) {
363             ILispDAO srcDstDao = getSDInnerDao(key, table);
364             return srcDstDao.getSpecific(SourceDestKeyHelper.getSrcBinary(key), subKey);
365         } else {
366             return table.getSpecific(key, subKey);
367         }
368     }
369
370     @Override
371     public void removeData(Eid eid, String subKey) {
372         Eid key = MaskUtil.normalize(eid);
373         ILispDAO table = getVniTable(key);
374         if (table == null) {
375             return;
376         }
377         if (key.getAddress() instanceof SourceDestKey) {
378             ILispDAO db = getSDInnerDao(key, table);
379             if (db != null) {
380                 db.removeSpecific(SourceDestKeyHelper.getSrcBinary(key), subKey);
381             }
382         } else {
383             table.removeSpecific(key, subKey);
384         }
385     }
386 }