Checkstyle: fix issues and enforce on implementation
[lispflowmapping.git] / mappingservice / implementation / src / main / java / org / opendaylight / lispflowmapping / implementation / lisp / MapServer.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.implementation.lisp;
10
11 import com.google.common.base.Preconditions;
12 import java.net.InetAddress;
13 import java.net.NetworkInterface;
14 import java.net.SocketException;
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.Enumeration;
18 import java.util.HashSet;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Objects;
22 import java.util.Set;
23 import org.apache.commons.lang3.BooleanUtils;
24 import org.opendaylight.controller.md.sal.binding.api.NotificationService;
25 import org.opendaylight.lispflowmapping.implementation.config.ConfigIni;
26 import org.opendaylight.lispflowmapping.interfaces.dao.SubKeys;
27 import org.opendaylight.lispflowmapping.interfaces.dao.SubscriberRLOC;
28 import org.opendaylight.lispflowmapping.interfaces.lisp.IMapNotifyHandler;
29 import org.opendaylight.lispflowmapping.interfaces.lisp.IMapServerAsync;
30 import org.opendaylight.lispflowmapping.interfaces.mappingservice.IMappingService;
31 import org.opendaylight.lispflowmapping.lisp.authentication.LispAuthenticationUtil;
32 import org.opendaylight.lispflowmapping.lisp.type.LispMessage;
33 import org.opendaylight.lispflowmapping.lisp.util.LispAddressStringifier;
34 import org.opendaylight.lispflowmapping.lisp.util.LispAddressUtil;
35 import org.opendaylight.lispflowmapping.lisp.util.MapNotifyBuilderHelper;
36 import org.opendaylight.lispflowmapping.lisp.util.MapRequestUtil;
37 import org.opendaylight.lispflowmapping.lisp.util.SourceDestKeyHelper;
38 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
39 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.lisp.address.types.rev151105.lisp.address.address.SourceDestKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.inet.binary.types.rev160303.IpAddressBinary;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.MapRegister;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.SiteId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.container.Eid;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.list.EidItem;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.eid.list.EidItemBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapnotifymessage.MapNotifyBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.authkey.container.MappingAuthkey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecord;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.container.MappingRecordBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.list.MappingRecordItem;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.mapping.record.list.MappingRecordItemBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.maprequestnotification.MapRequestBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.transport.address.TransportAddress;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.lisp.proto.rev151105.transport.address.TransportAddressBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChange;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingChanged;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.MappingOrigin;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.lfm.mappingservice.rev150906.OdlMappingserviceListener;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61
62 public class MapServer implements IMapServerAsync, OdlMappingserviceListener {
63
64     protected static final Logger LOG = LoggerFactory.getLogger(MapServer.class);
65     private static final byte[] ALL_ZEROES_XTR_ID = new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ,0};
66     private IMappingService mapService;
67     private boolean subscriptionService;
68     private IMapNotifyHandler notifyHandler;
69     private NotificationService notificationService;
70
71     public MapServer(IMappingService mapService, boolean subscriptionService,
72             IMapNotifyHandler notifyHandler, NotificationService notificationService) {
73         Preconditions.checkNotNull(mapService);
74         this.mapService = mapService;
75         this.subscriptionService = subscriptionService;
76         this.notifyHandler = notifyHandler;
77         this.notificationService = notificationService;
78         if (notificationService != null) {
79             notificationService.registerNotificationListener(this);
80         }
81     }
82
83     @Override
84     public void setSubscriptionService(boolean subscriptionService) {
85         this.subscriptionService = subscriptionService;
86     }
87
88     @SuppressWarnings("unchecked")
89     public void handleMapRegister(MapRegister mapRegister) {
90         boolean mappingUpdated = false;
91         boolean merge = ConfigIni.getInstance().mappingMergeIsSet() && mapRegister.isMergeEnabled();
92         Set<SubscriberRLOC> subscribers = null;
93         MappingRecord oldMapping;
94
95         if (merge) {
96             if (!mapRegister.isXtrSiteIdPresent() || mapRegister.getXtrId() == null) {
97                 LOG.error("Merge bit is set in Map-Register, but xTR-ID is not present. Will not merge.");
98                 merge = false;
99             } else if (Arrays.equals(mapRegister.getXtrId().getValue(), ALL_ZEROES_XTR_ID)) {
100                 LOG.warn("Merge bit is set in Map-Register, but xTR-ID is all zeroes.");
101             }
102         }
103
104         for (MappingRecordItem record : mapRegister.getMappingRecordItem()) {
105             MappingRecord mapping = record.getMappingRecord();
106
107             oldMapping = (MappingRecord) mapService.getMapping(MappingOrigin.Southbound, mapping.getEid());
108             mapService.addMapping(MappingOrigin.Southbound, mapping.getEid(), getSiteId(mapRegister), mapping, merge);
109
110             if (subscriptionService) {
111                 MappingRecord newMapping = merge
112                         ? (MappingRecord) mapService.getMapping(MappingOrigin.Southbound, mapping.getEid()) : mapping;
113
114                 if (mappingChanged(oldMapping, newMapping)) {
115                     if (LOG.isDebugEnabled()) {
116                         LOG.debug("Mapping update occured for {} SMRs will be sent for its subscribers.",
117                                 LispAddressStringifier.getString(mapping.getEid()));
118                     }
119                     subscribers = getSubscribers(mapping.getEid());
120                     sendSmrs(mapping, subscribers);
121                     mappingUpdated = true;
122                 }
123             }
124         }
125         if (BooleanUtils.isTrue(mapRegister.isWantMapNotify())) {
126             LOG.trace("MapRegister wants MapNotify");
127             MapNotifyBuilder builder = new MapNotifyBuilder();
128             List<TransportAddress> rlocs = null;
129             if (merge) {
130                 Set<IpAddressBinary> notifyRlocs = new HashSet<IpAddressBinary>();
131                 List<MappingRecordItem> mergedMappings = new ArrayList<MappingRecordItem>();
132                 for (MappingRecordItem record : mapRegister.getMappingRecordItem()) {
133                     MappingRecord mapping = record.getMappingRecord();
134                     MappingRecord currentRecord = (MappingRecord) mapService.getMapping(MappingOrigin.Southbound,
135                             mapping.getEid());
136                     mergedMappings.add(new MappingRecordItemBuilder().setMappingRecord(currentRecord).build());
137                     Set<IpAddressBinary> sourceRlocs = (Set<IpAddressBinary>) mapService.getData(
138                             MappingOrigin.Southbound, mapping.getEid(), SubKeys.SRC_RLOCS);
139                     if (sourceRlocs != null) {
140                         notifyRlocs.addAll(sourceRlocs);
141                     }
142                 }
143                 MapNotifyBuilderHelper.setFromMapRegisterAndMappingRecordItems(builder, mapRegister, mergedMappings);
144                 // send map-notify to merge group only when mapping record is changed
145                 if (mappingUpdated) {
146                     rlocs = getTransportAddresses(notifyRlocs);
147                 }
148             } else {
149                 MapNotifyBuilderHelper.setFromMapRegister(builder, mapRegister);
150             }
151             List<MappingRecordItem> mappings = builder.getMappingRecordItem();
152             if (mappings != null && mappings.get(0) != null && mappings.get(0).getMappingRecord() != null
153                     && mappings.get(0).getMappingRecord().getEid() != null) {
154                 MappingAuthkey authkey = mapService.getAuthenticationKey(mappings.get(0).getMappingRecord().getEid());
155                 if (authkey != null) {
156                     builder.setAuthenticationData(LispAuthenticationUtil.createAuthenticationData(builder.build(),
157                             authkey.getKeyString()));
158                 }
159             }
160             notifyHandler.handleMapNotify(builder.build(), rlocs);
161         }
162     }
163
164     private static List<TransportAddress> getTransportAddresses(Set<IpAddressBinary> addresses) {
165         List<TransportAddress> rlocs = new ArrayList<TransportAddress>();
166         for (IpAddressBinary address : addresses) {
167             TransportAddressBuilder tab = new TransportAddressBuilder();
168             tab.setIpAddress(address);
169             tab.setPort(new PortNumber(LispMessage.PORT_NUM));
170             rlocs.add(tab.build());
171         }
172         return rlocs;
173     }
174
175     private SiteId getSiteId(MapRegister mapRegister) {
176         return (mapRegister.getSiteId() != null) ? new SiteId(mapRegister.getSiteId()) : null;
177     }
178
179     @Override
180     public void onMappingChanged(MappingChanged notification) {
181         if (subscriptionService) {
182             sendSmrs(notification.getMappingRecord(), getSubscribers(notification.getMappingRecord().getEid()));
183             if (notification.getChangeType().equals(MappingChange.Removed)) {
184                 removeSubscribers(notification.getMappingRecord().getEid());
185             }
186         }
187     }
188
189     private static boolean mappingChanged(MappingRecord oldMapping, MappingRecord newMapping) {
190         // We only check for fields we care about
191         // XXX: This code needs to be checked and updated when the YANG model is modified
192         Preconditions.checkNotNull(newMapping, "The new mapping should never be null");
193         if (oldMapping == null) {
194             LOG.trace("mappingChanged(): old mapping is null");
195             return true;
196         } else if (!Objects.equals(oldMapping.getEid(), newMapping.getEid())) {
197             LOG.trace("mappingChanged(): EID");
198             return true;
199         } else if (!Objects.equals(oldMapping.getLocatorRecord(), newMapping.getLocatorRecord())) {
200             LOG.trace("mappingChanged(): RLOC");
201             return true;
202         } else if (!Objects.equals(oldMapping.getAction(), newMapping.getAction())) {
203             LOG.trace("mappingChanged(): action");
204             return true;
205         } else if (!Objects.equals(oldMapping.getRecordTtl(), newMapping.getRecordTtl())) {
206             LOG.trace("mappingChanged(): TTL");
207             return true;
208         } else if (!Objects.equals(oldMapping.getMapVersion(), newMapping.getMapVersion())) {
209             LOG.trace("mappingChanged(): mapping version");
210             return true;
211         }
212         return false;
213     }
214
215     private void sendSmrs(MappingRecord record, Set<SubscriberRLOC> subscribers) {
216         Eid eid = record.getEid();
217         handleSmr(eid, subscribers, notifyHandler);
218
219         // For SrcDst LCAF also send SMRs to Dst prefix
220         if (eid.getAddress() instanceof SourceDestKey) {
221             Eid dstAddr = SourceDestKeyHelper.getDstBinary(eid);
222             Set<SubscriberRLOC> dstSubs = getSubscribers(dstAddr);
223             MappingRecord newRecord = new MappingRecordBuilder(record).setEid(dstAddr).build();
224             handleSmr(newRecord.getEid(), dstSubs, notifyHandler);
225         }
226     }
227
228     @SuppressWarnings("checkstyle:IllegalCatch")
229     private void handleSmr(Eid eid, Set<SubscriberRLOC> subscribers, IMapNotifyHandler callback) {
230         if (subscribers == null) {
231             return;
232         }
233         MapRequestBuilder mrb = MapRequestUtil.prepareSMR(eid, LispAddressUtil.toRloc(getLocalAddress()));
234         LOG.trace("Built SMR packet: " + mrb.build().toString());
235         // Using Iterator ensures that we don't get a ConcurrentModificationException when removing a SubscriberRLOC
236         // from a Set.
237         Iterator<SubscriberRLOC> iterator = subscribers.iterator();
238         while (iterator.hasNext()) {
239             SubscriberRLOC subscriber = iterator.next();
240             if (subscriber.timedOut()) {
241                 LOG.trace("Lazy removing expired subscriber entry " + subscriber.toString());
242                 iterator.remove();
243             } else {
244                 try {
245                     // The address stored in the SMR's EID record is used as Source EID in the SMR-invoked Map-Request.
246                     // To ensure consistent behavior it is set to the value used to originally request a given mapping
247                     mrb.setEidItem(new ArrayList<EidItem>());
248                     mrb.getEidItem().add(new EidItemBuilder().setEid(subscriber.getSrcEid()).build());
249                     callback.handleSMR(mrb.build(), subscriber.getSrcRloc());
250                 } catch (Exception e) {
251                     LOG.error("Errors encountered while handling SMR:", e);
252                 }
253             }
254         }
255         addSubscribers(eid, subscribers);
256     }
257
258     @SuppressWarnings("unchecked")
259     private Set<SubscriberRLOC> getSubscribers(Eid address) {
260         return (Set<SubscriberRLOC>) mapService.getData(MappingOrigin.Southbound, address, SubKeys.SUBSCRIBERS);
261     }
262
263     private void removeSubscribers(Eid address) {
264         mapService.removeData(MappingOrigin.Southbound, address, SubKeys.SUBSCRIBERS);
265     }
266
267     private void addSubscribers(Eid address, Set<SubscriberRLOC> subscribers) {
268         mapService.addData(MappingOrigin.Southbound, address, SubKeys.SUBSCRIBERS, subscribers);
269     }
270
271     private static InetAddress getLocalAddress() {
272         try {
273             Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
274             while (interfaces.hasMoreElements()) {
275                 NetworkInterface current = interfaces.nextElement();
276                 LOG.debug("Interface " + current.toString());
277                 if (!current.isUp() || current.isLoopback() || current.isVirtual()) {
278                     continue;
279                 }
280                 Enumeration<InetAddress> addresses = current.getInetAddresses();
281                 while (addresses.hasMoreElements()) {
282                     InetAddress currentAddr = addresses.nextElement();
283                     // Skip loopback and link local addresses
284                     if (currentAddr.isLoopbackAddress() || currentAddr.isLinkLocalAddress()) {
285                         continue;
286                     }
287                     LOG.debug(currentAddr.getHostAddress());
288                     return currentAddr;
289                 }
290             }
291         } catch (SocketException se) {
292             LOG.debug("Caught socket exceptio", se);
293         }
294         return null;
295     }
296 }