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