IPv4 entries appear in FIB though not asso to Rout
[netvirt.git] / vpnmanager / impl / src / main / java / org / opendaylight / netvirt / vpnmanager / iplearn / AbstractIpLearnNotificationHandler.java
1 /*
2  * Copyright (c) 2018 Alten Calsoft Labs India 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
9 package org.opendaylight.netvirt.vpnmanager.iplearn;
10
11 import com.google.common.base.Optional;
12 import com.google.common.cache.Cache;
13 import com.google.common.cache.CacheBuilder;
14 import java.math.BigInteger;
15 import java.util.List;
16 import java.util.Objects;
17 import java.util.concurrent.TimeUnit;
18 import org.apache.commons.lang3.tuple.ImmutablePair;
19 import org.apache.commons.lang3.tuple.Pair;
20 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
21 import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
22 import org.opendaylight.genius.mdsalutil.NWUtil;
23 import org.opendaylight.netvirt.neutronvpn.api.enums.IpVersionChoice;
24 import org.opendaylight.netvirt.vpnmanager.VpnUtil;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix;
27 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefixBuilder;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.MacAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.LearntVpnVipToPortEventAction;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.adjacency.list.Adjacency;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.l3vpn.rev130911.learnt.vpn.vip.to.port.data.LearntVpnVipToPort;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.neutron.vpn.portip.port.data.VpnPortipToPort;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.neutronvpn.rev150602.subnetmaps.Subnetmap;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.netvirt.vpn.config.rev161130.VpnConfig;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39
40 public abstract class AbstractIpLearnNotificationHandler {
41
42     private static final Logger LOG = LoggerFactory.getLogger(AbstractIpLearnNotificationHandler.class);
43
44     // temp where Key is VPNInstance+IP and value is timestamp
45     private final Cache<Pair<String, String>, BigInteger> migrateIpCache;
46
47     protected final DataBroker dataBroker;
48     protected final IdManagerService idManager;
49     protected final IInterfaceManager interfaceManager;
50     protected final VpnConfig config;
51     protected final VpnUtil vpnUtil;
52
53     public AbstractIpLearnNotificationHandler(DataBroker dataBroker, IdManagerService idManager,
54             IInterfaceManager interfaceManager, VpnConfig vpnConfig, VpnUtil vpnUtil) {
55         this.dataBroker = dataBroker;
56         this.idManager = idManager;
57         this.interfaceManager = interfaceManager;
58         this.config = vpnConfig;
59         this.vpnUtil = vpnUtil;
60
61         long duration = config.getIpLearnTimeout() * 10;
62         long cacheSize = config.getMigrateIpCacheSize().longValue();
63         migrateIpCache =
64                 CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterWrite(duration,
65                         TimeUnit.MILLISECONDS).build();
66     }
67
68     protected void validateAndProcessIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac,
69             IpAddress targetIP, BigInteger metadata) {
70         List<Adjacency> adjacencies = vpnUtil.getAdjacenciesForVpnInterfaceFromConfig(srcInterface);
71         IpVersionChoice srcIpVersion = vpnUtil.getIpVersionFromString(srcIP.stringValue());
72         Uuid srcIpSubnetId = null;
73         if (adjacencies != null) {
74             for (Adjacency adj : adjacencies) {
75                 IpPrefix ipPrefix = IpPrefixBuilder.getDefaultInstance(adj.getIpAddress());
76                 // If extra/static route is configured, we should ignore for learning process
77                 if (NWUtil.isIpAddressInRange(srcIP, ipPrefix)) {
78                     return;
79                 }
80                 IpVersionChoice currentAdjIpVersion = vpnUtil.getIpVersionFromString(adj.getIpAddress());
81                 if (srcIpVersion.isIpVersionChosen(currentAdjIpVersion)) {
82                     srcIpSubnetId = adj.getSubnetId();
83                 }
84             }
85         }
86
87         LOG.trace("ARP/NA Notification Response Received from interface {} and IP {} having MAC {}, learning MAC",
88                 srcInterface, srcIP.stringValue(), srcMac.getValue());
89         processIpLearning(srcInterface, srcIP, srcMac, metadata, targetIP, srcIpSubnetId);
90     }
91
92     protected void processIpLearning(String srcInterface, IpAddress srcIP, MacAddress srcMac, BigInteger metadata,
93             IpAddress dstIP, Uuid srcIpSubnetId) {
94         if (metadata != null && !Objects.equals(metadata, BigInteger.ZERO)) {
95             Optional<List<String>> vpnList = vpnUtil.getVpnHandlingIpv4AssociatedWithInterface(srcInterface);
96             if (vpnList.isPresent()) {
97                 String srcIpToQuery = srcIP.stringValue();
98                 String destIpToQuery = dstIP.stringValue();
99                 for (String vpnName : vpnList.get()) {
100                     LOG.info("Received ARP/NA for sender MAC {} and sender IP {} via interface {}",
101                               srcMac.getValue(), srcIpToQuery, srcInterface);
102                     VpnPortipToPort vpnPortipToPort =
103                             vpnUtil.getNeutronPortFromVpnPortFixedIp(vpnName, srcIpToQuery);
104                     if (vpnPortipToPort != null) {
105                         /* This is a well known neutron port and so should be ignored
106                          * from being discovered
107                          */
108                         continue;
109                     }
110                     if (srcIpSubnetId != null) {
111                         Subnetmap snMap = vpnUtil.getSubnetmapFromItsUuid(srcIpSubnetId);
112                         if (snMap != null && snMap.getVpnId() == null) {
113                             /* If the subnet is not part of vpn then it should be ignored
114                              * from being discovered. This use case will come for dual stack
115                              * network. i.e V6 or V4 subnet only part of VPN.
116                              */
117                             continue;
118                         }
119                     }
120                     LearntVpnVipToPort learntVpnVipToPort = vpnUtil.getLearntVpnVipToPort(vpnName, srcIpToQuery);
121                     if (learntVpnVipToPort != null) {
122                         String oldPortName = learntVpnVipToPort.getPortName();
123                         String oldMac = learntVpnVipToPort.getMacAddress();
124                         if (!oldMac.equalsIgnoreCase(srcMac.getValue())) {
125                             //MAC has changed for requested IP
126                             LOG.info("ARP/NA Source IP/MAC data modified for IP {} with MAC {} and Port {}",
127                                     srcIpToQuery, srcMac, srcInterface);
128                             synchronized ((vpnName + srcIpToQuery).intern()) {
129                                 vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery,
130                                         oldPortName, oldMac, LearntVpnVipToPortEventAction.Delete, null);
131                                 putVpnIpToMigrateIpCache(vpnName, srcIpToQuery, srcMac);
132                             }
133                         }
134                     } else if (!isIpInMigrateCache(vpnName, srcIpToQuery)) {
135                         learnMacFromIncomingPacket(vpnName, srcInterface, srcIP, srcMac, dstIP);
136                     }
137                 }
138             } else {
139                 LOG.info("IP LEARN NO_RESOLVE: VPN  not configured. Ignoring responding to ARP/NA requests from this"
140                         + " Interface {}.", srcInterface);
141                 return;
142
143             }
144         }
145     }
146
147     private void learnMacFromIncomingPacket(String vpnName, String srcInterface, IpAddress srcIP, MacAddress srcMac,
148             IpAddress dstIP) {
149         String srcIpToQuery = srcIP.stringValue();
150         String destIpToQuery = dstIP.stringValue();
151         synchronized ((vpnName + srcIpToQuery).intern()) {
152             vpnUtil.createLearntVpnVipToPortEvent(vpnName, srcIpToQuery, destIpToQuery, srcInterface,
153                     srcMac.getValue(), LearntVpnVipToPortEventAction.Add, null);
154         }
155     }
156
157     private void putVpnIpToMigrateIpCache(String vpnName, String ipToQuery, MacAddress srcMac) {
158         long cacheSize = config.getMigrateIpCacheSize().longValue();
159         if (migrateIpCache.size() >= cacheSize) {
160             LOG.debug("IP_MIGRATE_CACHE: max size {} reached, assuming cache eviction we still put IP {}"
161                     + " vpnName {} with MAC {}", cacheSize, ipToQuery, vpnName, srcMac);
162         }
163         LOG.debug("IP_MIGRATE_CACHE: add to dirty cache IP {} vpnName {} with MAC {}", ipToQuery, vpnName, srcMac);
164         migrateIpCache.put(new ImmutablePair<>(vpnName, ipToQuery),
165                 new BigInteger(String.valueOf(System.currentTimeMillis())));
166     }
167
168     private boolean isIpInMigrateCache(String vpnName, String ipToQuery) {
169         if (migrateIpCache == null || migrateIpCache.size() == 0) {
170             return false;
171         }
172         Pair<String, String> keyPair = new ImmutablePair<>(vpnName, ipToQuery);
173         BigInteger prevTimeStampCached = migrateIpCache.getIfPresent(keyPair);
174         if (prevTimeStampCached == null) {
175             LOG.debug("IP_MIGRATE_CACHE: there is no IP {} vpnName {} in dirty cache, so learn it",
176                     ipToQuery, vpnName);
177             return false;
178         }
179         if (System.currentTimeMillis() > prevTimeStampCached.longValue() + config.getIpLearnTimeout()) {
180             LOG.debug("IP_MIGRATE_CACHE: older than timeout value - remove from dirty cache IP {} vpnName {}",
181                     ipToQuery, vpnName);
182             migrateIpCache.invalidate(keyPair);
183             return false;
184         }
185         LOG.debug("IP_MIGRATE_CACHE: younger than timeout value - ignore learning IP {} vpnName {}",
186                 ipToQuery, vpnName);
187         return true;
188     }
189 }