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