Fix build faliures due to OFPlugin checktyle fixes
[netvirt.git] / vpnservice / vpnmanager / vpnmanager-impl / src / main / java / org / opendaylight / netvirt / vpnmanager / ArpNotificationHandler.java
1 /*
2  * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. 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.netvirt.vpnmanager;
9
10 import com.google.common.base.Optional;
11 import com.google.common.cache.Cache;
12 import com.google.common.cache.CacheBuilder;
13 import java.math.BigInteger;
14 import java.net.UnknownHostException;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.concurrent.TimeUnit;
20 import javax.inject.Inject;
21 import javax.inject.Singleton;
22 import org.apache.commons.lang3.tuple.ImmutablePair;
23 import org.apache.commons.lang3.tuple.Pair;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
26 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
27 import org.opendaylight.genius.mdsalutil.MDSALUtil;
28 import org.opendaylight.genius.mdsalutil.NWUtil;
29 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.VpnInterfaces;
30 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterface;
31 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceBuilder;
32 import org.opendaylight.yang.gen.v1.urn.huawei.params.xml.ns.yang.l3vpn.rev140815.vpn.interfaces.VpnInterfaceKey;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
34 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.PhysAddress;
35 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpRequestReceived;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.ArpResponseReceived;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.MacChanged;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.arputil.rev160406.OdlArputilListener;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.Adjacencies;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.AdjacenciesOp;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.VpnInterfaceOpData;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency.AdjacencyType;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.AdjacencyKey;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntry;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.vpn._interface.op.data.VpnInterfaceOpDataEntryKey;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 @Singleton
58 public class ArpNotificationHandler implements OdlArputilListener {
59     private static final Logger LOG = LoggerFactory.getLogger(ArpNotificationHandler.class);
60     // temp where Key is VPNInstance+IP and value is timestamp
61     private final Cache<Pair<String, String>, BigInteger> migrateArpCache;
62
63     private final DataBroker dataBroker;
64     private final IdManagerService idManager;
65     private final IInterfaceManager interfaceManager;
66     private final VpnConfig config;
67
68     @Inject
69     public ArpNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
70                                   IInterfaceManager interfaceManager, VpnConfig vpnConfig) {
71         this.dataBroker = dataBroker;
72         this.idManager = idManager;
73         this.interfaceManager = interfaceManager;
74         this.config = vpnConfig;
75
76         long duration = config.getArpLearnTimeout() * 10;
77         long cacheSize = config.getArpCacheSize().longValue();
78         migrateArpCache =
79                 CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterWrite(duration,
80                         TimeUnit.MILLISECONDS).build();
81     }
82
83     @Override
84     public void onMacChanged(MacChanged notification) {
85
86     }
87
88     @Override
89     public void onArpRequestReceived(ArpRequestReceived notification) {
90         String srcInterface = notification.getInterface();
91         IpAddress srcIP = notification.getSrcIpaddress();
92         PhysAddress srcMac = notification.getSrcMac();
93         IpAddress targetIP = notification.getDstIpaddress();
94         BigInteger metadata = notification.getMetadata();
95         boolean isGarp = srcIP.equals(targetIP);
96         if (!isGarp) {
97             LOG.info("ArpNotification Non-Gratuitous Request Received from "
98                       + "interface {} and IP {} having MAC {} target destination {}, ignoring..",
99                     srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
100                     targetIP.getIpv4Address().getValue());
101             return;
102         }
103         LOG.info("ArpNotification Gratuitous Request Received from "
104                   + "interface {} and IP {} having MAC {} target destination {}, learning MAC",
105                   srcInterface, srcIP.getIpv4Address().getValue(),srcMac.getValue(),
106                   targetIP.getIpv4Address().getValue());
107         processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
108     }
109
110     @Override
111     public void onArpResponseReceived(ArpResponseReceived notification) {
112         String srcInterface = notification.getInterface();
113         IpAddress srcIP = notification.getSrcIpaddress();
114         PhysAddress srcMac = notification.getSrcMac();
115         LOG.info("ArpNotification Response Received from interface {} and IP {} having MAC {}, learning MAC",
116                 srcInterface, srcIP.getIpv4Address().getValue(), srcMac.getValue());
117         List<Adjacency> adjacencies = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker, srcInterface);
118         if (adjacencies != null) {
119             for (Adjacency adj : adjacencies) {
120                 String ipAddress = adj.getIpAddress();
121                 try {
122                     if (NWUtil.isIpInSubnet(NWUtil.ipAddressToInt(srcIP.getIpv4Address().getValue()), ipAddress)) {
123                         return;
124                     }
125                 } catch (UnknownHostException e) {
126                     LOG.error("Subnet string {} not convertible to InetAdddress", srcIP, e);
127                 }
128             }
129         }
130         BigInteger metadata = notification.getMetadata();
131         IpAddress targetIP = notification.getDstIpaddress();
132         LOG.trace("ArpNotification Response Received from interface {} and IP {} having MAC {}, learning MAC",
133                 srcInterface, srcIP.getIpv4Address().getValue(), srcMac.getValue());
134         processArpLearning(srcInterface, srcIP, srcMac, metadata, targetIP);
135     }
136
137     private void processArpLearning(String srcInterface, IpAddress srcIP, PhysAddress srcMac, BigInteger metadata,
138             IpAddress dstIP) {
139         if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
140             Optional<List<String>> vpnList = VpnUtil
141                   .getVpnHandlingIpv4AssociatedWithInterface(dataBroker, srcInterface);
142             if (vpnList.isPresent()) {
143                 for (String vpnName : vpnList.get()) {
144                     LOG.info("Received ARP for sender MAC {} and sender IP {} via interface {}",
145                               srcMac.getValue(), srcIP.getIpv4Address().getValue(), srcInterface);
146                     String ipToQuery = srcIP.getIpv4Address().getValue();
147                     LOG.info("ARP being processed for Source IP {}", ipToQuery);
148                     VpnPortipToPort vpnPortipToPort =
149                             VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ipToQuery);
150                     if (vpnPortipToPort != null) {
151                         /* This is a well known neutron port and so should be ignored
152                          * from being discovered
153                          */
154                         continue;
155                     }
156                     LearntVpnVipToPort learntVpnVipToPort = VpnUtil.getLearntVpnVipToPort(dataBroker,
157                               vpnName, ipToQuery);
158                     if (learntVpnVipToPort != null) {
159                         String oldPortName = learntVpnVipToPort.getPortName();
160                         String oldMac = learntVpnVipToPort.getMacAddress();
161                         if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
162                             //MAC has changed for requested IP
163                             LOG.info("ARP Source IP/MAC data modified for IP {} with MAC {} and Port {}",
164                                     ipToQuery, srcMac, srcInterface);
165                             synchronized ((vpnName + ipToQuery).intern()) {
166                                 removeMipAdjacency(vpnName, oldPortName, srcIP);
167                                 VpnUtil.removeLearntVpnVipToPort(dataBroker, vpnName, ipToQuery);
168                                 putVpnIpToMigrateArpCache(vpnName, ipToQuery, srcMac);
169                             }
170                         }
171                     } else if (!isIpInArpMigrateCache(vpnName, ipToQuery)) {
172                         learnMacFromArpPackets(vpnName, srcInterface, srcIP, srcMac, dstIP);
173                     }
174                 }
175             } else {
176                 LOG.info("ARP NO_RESOLVE: VPN  not configured. Ignoring responding to ARP requests from this"
177                         + " Interface {}.", srcInterface);
178                 return;
179
180             }
181         }
182     }
183
184     private void learnMacFromArpPackets(String vpnName, String srcInterface,
185         IpAddress srcIP, PhysAddress srcMac, IpAddress dstIP) {
186         String ipToQuery = srcIP.getIpv4Address().getValue();
187         synchronized ((vpnName + ipToQuery).intern()) {
188             VpnUtil.createLearntVpnVipToPort(dataBroker, vpnName, ipToQuery, srcInterface, srcMac.getValue());
189             addMipAdjacency(vpnName, srcInterface, srcIP, srcMac.getValue(), dstIP);
190         }
191     }
192
193     private void addMipAdjacency(String vpnName, String vpnInterface, IpAddress srcPrefix, String mipMacAddress,
194             IpAddress dstPrefix) {
195         LOG.trace("Adding {} adjacency to VPN Interface {} ",srcPrefix,vpnInterface);
196         InstanceIdentifier<VpnInterface> vpnIfId = VpnUtil.getVpnInterfaceIdentifier(vpnInterface);
197         InstanceIdentifier<Adjacencies> path = vpnIfId.augmentation(Adjacencies.class);
198         synchronized (vpnInterface.intern()) {
199             Optional<Adjacencies> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, path);
200             String nextHopIpAddr = null;
201             String nextHopMacAddress = null;
202             String ip = srcPrefix.getIpv4Address().getValue();
203             if (interfaceManager.isExternalInterface(vpnInterface)) {
204                 String subnetId = getSubnetId(vpnName, dstPrefix.getIpv4Address().getValue());
205                 if (subnetId == null) {
206                     LOG.trace("Can't find corresponding subnet for src IP {}, src MAC {}, dst IP {},  in VPN {}",
207                             srcPrefix, mipMacAddress, dstPrefix, vpnName);
208                     return;
209                 }
210                 ip = VpnUtil.getIpPrefix(ip);
211                 AdjacencyBuilder newAdjBuilder = new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip))
212                         .setAdjacencyType(AdjacencyType.PrimaryAdjacency).setMacAddress(mipMacAddress)
213                         .setSubnetId(new Uuid(subnetId)).setPhysNetworkFunc(true);
214
215                 List<Adjacency> adjacencyList = adjacencies.isPresent()
216                         ? adjacencies.get().getAdjacency() : new ArrayList<>();
217
218                 adjacencyList.add(newAdjBuilder.build());
219
220                 Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
221                 Optional<VpnInterface> optionalVpnInterface =
222                     VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId);
223                 VpnInterface newVpnIntf;
224                 if (optionalVpnInterface.isPresent()) {
225                     newVpnIntf =
226                         new VpnInterfaceBuilder(optionalVpnInterface.get())
227                             .addAugmentation(Adjacencies.class, aug)
228                             .build();
229                     VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId, newVpnIntf);
230                 }
231                 LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
232                 return;
233             }
234
235             if (adjacencies.isPresent()) {
236                 List<Adjacency> adjacencyList = adjacencies.get().getAdjacency();
237                 ip = VpnUtil.getIpPrefix(ip);
238                 for (Adjacency adjacs : adjacencyList) {
239                     if (adjacs.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
240                         if (adjacs.getIpAddress().equals(ip)) {
241                             LOG.error("The MIP {} is already present as a primary adjacency for interface {} vpn {}."
242                                     + "Skipping adjacency addition.", ip, vpnInterface, vpnName);
243                             return;
244                         }
245                         nextHopIpAddr = adjacs.getIpAddress();
246                         nextHopMacAddress = adjacs.getMacAddress();
247                         break;
248                     }
249                 }
250                 if (nextHopIpAddr != null) {
251                     String rd = VpnUtil.getVpnRd(dataBroker, vpnName);
252                     long label =
253                         VpnUtil.getUniqueId(idManager, VpnConstants.VPN_IDPOOL_NAME,
254                             VpnUtil.getNextHopLabelKey(rd != null ? rd : vpnName, ip));
255                     if (label == 0) {
256                         LOG.error("Unable to fetch label from Id Manager. Bailing out of adding MIP adjacency {} "
257                             + "to vpn interface {} for vpn {}", ip, vpnInterface, vpnName);
258                         return;
259                     }
260                     String nextHopIp = nextHopIpAddr.split("/")[0];
261                     AdjacencyBuilder newAdjBuilder =
262                             new AdjacencyBuilder().setIpAddress(ip).setKey(new AdjacencyKey(ip)).setNextHopIpList(
263                                     Collections.singletonList(nextHopIp)).setAdjacencyType(AdjacencyType.LearntIp);
264                     if (mipMacAddress != null && !mipMacAddress.equalsIgnoreCase(nextHopMacAddress)) {
265                         newAdjBuilder.setMacAddress(mipMacAddress);
266                     }
267                     adjacencyList.add(newAdjBuilder.build());
268                     Adjacencies aug = VpnUtil.getVpnInterfaceAugmentation(adjacencyList);
269                     Optional<VpnInterface> optionalVpnInterface =
270                         VpnUtil.read(dataBroker, LogicalDatastoreType.CONFIGURATION, vpnIfId);
271                     VpnInterface newVpnIntf;
272                     if (optionalVpnInterface.isPresent()) {
273                         newVpnIntf =
274                             new VpnInterfaceBuilder(optionalVpnInterface.get())
275                                 .addAugmentation(Adjacencies.class, aug).build();
276                         VpnUtil.syncWrite(dataBroker, LogicalDatastoreType.CONFIGURATION,
277                                  vpnIfId, newVpnIntf);
278                     }
279                     LOG.debug(" Successfully stored subnetroute Adjacency into VpnInterface {}", vpnInterface);
280                 }
281             }
282         }
283
284     }
285
286     private String getSubnetId(String vpnName, String ip) {
287         // Check if this IP belongs to a router_interface
288         VpnPortipToPort vpnPortipToPort =
289                 VpnUtil.getNeutronPortFromVpnPortFixedIp(dataBroker, vpnName, ip);
290         if (vpnPortipToPort != null && vpnPortipToPort.isSubnetIp()) {
291             List<Adjacency> adjacecnyList = VpnUtil.getAdjacenciesForVpnInterfaceFromConfig(dataBroker,
292                     vpnPortipToPort.getPortName());
293             for (Adjacency adjacency : adjacecnyList) {
294                 if (adjacency.getAdjacencyType() == AdjacencyType.PrimaryAdjacency) {
295                     return adjacency.getSubnetId().getValue();
296                 }
297             }
298         }
299
300         // Check if this IP belongs to a router_gateway
301         List<Uuid> routerIds = VpnUtil.getExternalNetworkRouterIds(dataBroker, new Uuid(vpnName));
302         for (Uuid routerId : routerIds) {
303             Uuid subnetId = VpnUtil.getSubnetFromExternalRouterByIp(dataBroker, routerId, ip);
304             if (subnetId != null) {
305                 return subnetId.getValue();
306             }
307         }
308
309         return null;
310     }
311
312     private void removeMipAdjacency(String vpnName, String vpnInterface, IpAddress prefix) {
313         String ip = VpnUtil.getIpPrefix(prefix.getIpv4Address().getValue());
314         LOG.trace("Removing {} adjacency from Old VPN Interface {} ", ip, vpnInterface);
315         InstanceIdentifier<VpnInterfaceOpDataEntry> vpnIfId = VpnUtil.getVpnInterfaceOpDataEntryIdentifier(
316                                                               vpnInterface, vpnName);
317         InstanceIdentifier<AdjacenciesOp> path = vpnIfId.augmentation(AdjacenciesOp.class);
318         synchronized (vpnInterface.intern()) {
319             Optional<AdjacenciesOp> adjacencies = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL, path);
320             if (adjacencies.isPresent()) {
321                 InstanceIdentifier<Adjacency> adjacencyIdentifierOp =
322                     InstanceIdentifier.builder(VpnInterfaceOpData.class).child(VpnInterfaceOpDataEntry.class,
323                     new VpnInterfaceOpDataEntryKey(vpnInterface, vpnName)).augmentation(AdjacenciesOp.class)
324                         .child(Adjacency.class, new AdjacencyKey(ip)).build();
325                 Optional<Adjacency> adjacencyOper = VpnUtil.read(dataBroker, LogicalDatastoreType.OPERATIONAL,
326                         adjacencyIdentifierOp);
327                 InstanceIdentifier<Adjacency> adjacencyIdentifierConf =
328                         InstanceIdentifier.builder(VpnInterfaces.class).child(VpnInterface.class,
329                             new VpnInterfaceKey(vpnInterface)).augmentation(Adjacencies.class).child(Adjacency.class,
330                             new AdjacencyKey(ip)).build();
331                 if (adjacencyOper.isPresent()) {
332                     MDSALUtil.syncDelete(dataBroker, LogicalDatastoreType.CONFIGURATION, adjacencyIdentifierConf);
333                     LOG.info("Successfully deleted in configDS the learned-ip-adjacency for prefix {} on vpn {} for "
334                             + "interface {} for adjacency {}", ip, vpnName, vpnInterface, adjacencyOper);
335                 }
336             }
337         }
338     }
339
340     private void putVpnIpToMigrateArpCache(String vpnName, String ipToQuery, PhysAddress srcMac) {
341         long cacheSize = config.getArpCacheSize().longValue();
342         if (migrateArpCache.size() >= cacheSize) {
343             LOG.debug("ARP_MIGRATE_CACHE: max size {} reached, assuming cache eviction we still put IP {}"
344                     + " vpnName {} with MAC {}", cacheSize, ipToQuery, vpnName, srcMac);
345         }
346         LOG.debug("ARP_MIGRATE_CACHE: add to dirty cache IP {} vpnName {} with MAC {}", ipToQuery, vpnName, srcMac);
347         migrateArpCache.put(new ImmutablePair<>(vpnName, ipToQuery),
348                 new BigInteger(String.valueOf(System.currentTimeMillis())));
349     }
350
351     private boolean isIpInArpMigrateCache(String vpnName, String ipToQuery) {
352         if (migrateArpCache == null || migrateArpCache.size() == 0) {
353             return false;
354         }
355         Pair<String, String> keyPair = new ImmutablePair<>(vpnName, ipToQuery);
356         BigInteger prevTimeStampCached = migrateArpCache.getIfPresent(keyPair);
357         if (prevTimeStampCached == null) {
358             LOG.debug("ARP_MIGRATE_CACHE: there is no IP {} vpnName {} in dirty cache, so learn it",
359                     ipToQuery, vpnName);
360             return false;
361         }
362         if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getArpLearnTimeout()) {
363             LOG.debug("ARP_MIGRATE_CACHE: older than timeout value - remove from dirty cache IP {} vpnName {}",
364                     ipToQuery, vpnName);
365             migrateArpCache.invalidate(keyPair);
366             return false;
367         }
368         LOG.debug("ARP_MIGRATE_CACHE: younger than timeout value - ignore learning IP {} vpnName {}",
369                 ipToQuery, vpnName);
370         return true;
371     }
372 }